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
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>
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.
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.
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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_picture_pool.h>
33 #include <vlc_opengl.h>
36 // Define USE_OPENGL_ES to the GL ES Version you want to select
38 #if !defined (__APPLE__)
39 # if USE_OPENGL_ES == 2
40 # include <GLES2/gl2ext.h>
41 # elif USE_OPENGL_ES == 1
42 # include <GLES/glext.h>
44 # include <GL/glext.h>
47 # if USE_OPENGL_ES == 2
48 # include <OpenGLES/ES2/gl.h>
49 # elif USE_OPENGL_ES == 1
50 # include <OpenGLES/ES1/gl.h>
53 # include <OpenGL/glext.h>
58 #ifndef GL_UNSIGNED_SHORT_5_6_5
59 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
61 #ifndef GL_CLAMP_TO_EDGE
62 # define GL_CLAMP_TO_EDGE 0x812F
66 # define VLCGL_TEXTURE_COUNT 1
67 # define VLCGL_PICTURE_MAX 1
68 #elif defined(MACOS_OPENGL)
69 # define VLCGL_TEXTURE_COUNT 2
70 # define VLCGL_PICTURE_MAX 2
72 # define VLCGL_TEXTURE_COUNT 1
73 # define VLCGL_PICTURE_MAX 128
76 struct vout_display_opengl_t {
80 const vlc_chroma_description_t *chroma;
86 int tex_width[PICTURE_PLANE_MAX];
87 int tex_height[PICTURE_PLANE_MAX];
89 GLuint texture[VLCGL_TEXTURE_COUNT][PICTURE_PLANE_MAX];
95 GLfloat local_value[16][4];
97 /* fragment_program */
98 void (*GenProgramsARB)(GLsizei, GLuint *);
99 void (*BindProgramARB)(GLenum, GLuint);
100 void (*ProgramStringARB)(GLenum, GLenum, GLsizei, const GLvoid *);
101 void (*DeleteProgramsARB)(GLsizei, const GLuint *);
102 void (*ProgramLocalParameter4fvARB)(GLenum, GLuint, const GLfloat *);
105 void (*ActiveTextureARB)(GLenum);
106 void (*MultiTexCoord2fARB)(GLenum, GLfloat, GLfloat);
109 static inline int GetAlignedSize(unsigned size)
111 /* Return the smallest larger or equal power of 2 */
112 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
113 return ((align >> 1) == size) ? size : align;
116 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
117 const vlc_fourcc_t **subpicture_chromas,
120 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
125 if (vlc_gl_Lock(vgl->gl)) {
130 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
134 /* Load extensions */
135 bool supports_fp = false;
136 if (strstr(extensions, "GL_ARB_fragment_program")) {
137 vgl->GenProgramsARB = (void (*)(GLsizei, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glGenProgramsARB");
138 vgl->BindProgramARB = (void (*)(GLenum, GLuint))vlc_gl_GetProcAddress(vgl->gl, "glBindProgramARB");
139 vgl->ProgramStringARB = (void (*)(GLenum, GLenum, GLsizei, const GLvoid *))vlc_gl_GetProcAddress(vgl->gl, "glProgramStringARB");
140 vgl->DeleteProgramsARB = (void (*)(GLsizei, const GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgramsARB");
141 vgl->ProgramLocalParameter4fvARB = (void (*)(GLenum, GLuint, const GLfloat *))vlc_gl_GetProcAddress(vgl->gl, "glProgramLocalParameter4fvARB");
143 supports_fp = vgl->GenProgramsARB &&
144 vgl->BindProgramARB &&
145 vgl->ProgramStringARB &&
146 vgl->DeleteProgramsARB &&
147 vgl->ProgramLocalParameter4fvARB;
149 bool supports_multitexture = false;
150 if (strstr(extensions, "GL_ARB_multitexture")) {
151 vgl->ActiveTextureARB = (void (*)(GLenum))vlc_gl_GetProcAddress(vgl->gl, "glActiveTextureARB");
152 vgl->MultiTexCoord2fARB = (void (*)(GLenum, GLfloat, GLfloat))vlc_gl_GetProcAddress(vgl->gl, "glMultiTexCoord2fARB");
154 supports_multitexture = vgl->ActiveTextureARB &&
155 vgl->MultiTexCoord2fARB;
158 /* Initialize with default chroma */
161 vgl->fmt.i_chroma = VLC_CODEC_RGB16;
162 # if defined(WORDS_BIGENDIAN)
163 vgl->fmt.i_rmask = 0x001f;
164 vgl->fmt.i_gmask = 0x07e0;
165 vgl->fmt.i_bmask = 0xf800;
167 vgl->fmt.i_rmask = 0xf800;
168 vgl->fmt.i_gmask = 0x07e0;
169 vgl->fmt.i_bmask = 0x001f;
171 vgl->tex_target = GL_TEXTURE_2D;
172 vgl->tex_format = GL_RGB;
173 vgl->tex_type = GL_UNSIGNED_SHORT_5_6_5;
174 #elif defined(MACOS_OPENGL)
175 # if defined(WORDS_BIGENDIAN)
176 vgl->fmt.i_chroma = VLC_CODEC_YUYV;
178 vgl->fmt.i_chroma = VLC_CODEC_UYVY;
180 vgl->tex_target = GL_TEXTURE_RECTANGLE_EXT;
181 vgl->tex_format = GL_YCBCR_422_APPLE;
182 vgl->tex_type = GL_UNSIGNED_SHORT_8_8_APPLE;
184 vgl->fmt.i_chroma = VLC_CODEC_RGB32;
185 # if defined(WORDS_BIGENDIAN)
186 vgl->fmt.i_rmask = 0xff000000;
187 vgl->fmt.i_gmask = 0x00ff0000;
188 vgl->fmt.i_bmask = 0x0000ff00;
190 vgl->fmt.i_rmask = 0x000000ff;
191 vgl->fmt.i_gmask = 0x0000ff00;
192 vgl->fmt.i_bmask = 0x00ff0000;
194 vgl->tex_target = GL_TEXTURE_2D;
195 vgl->tex_format = GL_RGBA;
196 vgl->tex_type = GL_UNSIGNED_BYTE;
198 /* Use YUV if possible and needed */
199 bool need_fs_yuv = false;
200 if (supports_fp && supports_multitexture &&
201 vlc_fourcc_IsYUV(fmt->i_chroma) && !vlc_fourcc_IsYUV(vgl->fmt.i_chroma)) {
202 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
204 const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
205 if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
208 vgl->fmt.i_chroma = *list;
209 vgl->tex_format = GL_LUMINANCE;
210 vgl->tex_type = GL_UNSIGNED_BYTE;
217 vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
219 bool supports_npot = false;
220 #if USE_OPENGL_ES == 2
221 supports_npot = true;
222 #elif defined(MACOS_OPENGL)
223 supports_npot = true;
225 supports_npot |= strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != NULL ||
226 strstr(extensions, "GL_ARB_texture_non_power_of_two");
230 * TODO calculate the size such that the pictures can be used as
233 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
234 int w = vgl->fmt.i_width * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
235 int h = vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
237 vgl->tex_width[j] = w;
238 vgl->tex_height[j] = h;
241 /* A texture must have a size aligned on a power of 2 */
242 vgl->tex_width[j] = GetAlignedSize(w);
243 vgl->tex_height[j] = GetAlignedSize(h);
247 /* Build fragment program if needed */
249 vgl->local_count = 0;
254 /* [R/G/B][Y U V O] from TV range to full range
255 * XXX we could also do hue/brightness/constrast/gamma
256 * by simply changing the coefficients
258 const float matrix_bt601_tv2full[3][4] = {
259 { 1.1640, 0.0000, 1.4030, -0.7773 },
260 { 1.1640, -0.3440, -0.7140, 0.4580 },
261 { 1.1640, 1.7730, 0.0000, -0.9630 },
263 const float matrix_bt709_tv2full[3][4] = {
264 { 1.1640, 0.0000, 1.5701, -0.8612 },
265 { 1.1640, -0.1870, -0.4664, 0.2549 },
266 { 1.1640, 1.8556, 0.0000, -1.0045 },
268 const float (*matrix)[4] = fmt->i_height > 576 ? matrix_bt709_tv2full
269 : matrix_bt601_tv2full;
271 /* Basic linear YUV -> RGB conversion using bilinear interpolation */
272 const char *template_yuv =
274 "OPTION ARB_precision_hint_fastest;"
277 "TEX src.x, fragment.texcoord[0], texture[0], 2D;"
278 "TEX src.%c, fragment.texcoord[1], texture[1], 2D;"
279 "TEX src.%c, fragment.texcoord[2], texture[2], 2D;"
281 "PARAM coefficient[4] = { program.local[0..3] };"
284 "MAD tmp.rgb, src.xxxx, coefficient[0], coefficient[3];"
285 "MAD tmp.rgb, src.yyyy, coefficient[1], tmp;"
286 "MAD result.color.rgb, src.zzzz, coefficient[2], tmp;"
288 bool swap_uv = vgl->fmt.i_chroma == VLC_CODEC_YV12 ||
289 vgl->fmt.i_chroma == VLC_CODEC_YV9;
290 if (asprintf(&code, template_yuv,
292 swap_uv ? 'y' : 'z') < 0)
295 for (int i = 0; i < 4; i++)
296 for (int j = 0; j < 4; j++)
297 vgl->local_value[vgl->local_count + i][j] = j < 3 ? matrix[j][i] : 0.0;
298 vgl->local_count += 4;
301 vgl->GenProgramsARB(1, &vgl->program);
302 vgl->BindProgramARB(GL_FRAGMENT_PROGRAM_ARB, vgl->program);
303 vgl->ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
304 GL_PROGRAM_FORMAT_ASCII_ARB,
305 strlen(code), (const GLbyte*)code);
306 if (glGetError() == GL_INVALID_OPERATION) {
307 /* FIXME if the program was needed for YUV, the video will be broken */
310 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position);
312 const char *msg = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
313 fprintf(stderr, "GL_INVALID_OPERATION: error at %d: %s\n", position, msg);
315 vgl->DeleteProgramsARB(1, &vgl->program);
324 glDisable(GL_DEPTH_TEST);
325 glDepthMask(GL_FALSE);
326 glDisable(GL_CULL_FACE);
327 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
328 glClear(GL_COLOR_BUFFER_BIT);
330 vlc_gl_Unlock(vgl->gl);
333 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
334 for (int j = 0; j < PICTURE_PLANE_MAX; j++)
335 vgl->texture[i][j] = 0;
340 if (subpicture_chromas) {
341 *subpicture_chromas = NULL;
346 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
349 if (!vlc_gl_Lock(vgl->gl)) {
353 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
354 glDeleteTextures(vgl->chroma->plane_count, vgl->texture[i]);
357 vgl->DeleteProgramsARB(1, &vgl->program);
359 vlc_gl_Unlock(vgl->gl);
362 picture_pool_Delete(vgl->pool);
367 struct picture_sys_t {
368 vout_display_opengl_t *vgl;
373 static inline GLuint PictureGetTexture(picture_t *picture)
375 return *picture->p_sys->texture;
378 static int PictureLock(picture_t *picture)
383 vout_display_opengl_t *vgl = picture->p_sys->vgl;
384 if (!vlc_gl_Lock(vgl->gl)) {
385 glBindTexture(vgl->tex_target, PictureGetTexture(picture));
386 glTexSubImage2D(vgl->tex_target, 0,
387 0, 0, vgl->fmt.i_width, vgl->fmt.i_height,
388 vgl->tex_format, vgl->tex_type, picture->p[0].p_pixels);
390 vlc_gl_Unlock(vgl->gl);
395 static void PictureUnlock(picture_t *picture)
401 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
406 /* Allocate our pictures */
407 picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
410 for (count = 0; count < __MIN(VLCGL_PICTURE_MAX, requested_count); count++) {
411 picture[count] = picture_NewFromFormat(&vgl->fmt);
416 picture_sys_t *sys = picture[count]->p_sys = malloc(sizeof(*sys));
419 sys->texture = vgl->texture[count];
426 /* Wrap the pictures into a pool */
427 picture_pool_configuration_t cfg;
428 memset(&cfg, 0, sizeof(cfg));
429 cfg.picture_count = count;
430 cfg.picture = picture;
432 cfg.lock = PictureLock;
433 cfg.unlock = PictureUnlock;
435 vgl->pool = picture_pool_NewExtended(&cfg);
439 /* Allocates our textures */
440 if (vlc_gl_Lock(vgl->gl))
443 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
444 glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
445 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
446 if (vgl->chroma->plane_count > 1)
447 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
448 glBindTexture(vgl->tex_target, vgl->texture[i][j]);
451 /* Set the texture parameters */
452 glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
453 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
456 glTexParameteri(vgl->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
457 glTexParameteri(vgl->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
458 glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
459 glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
462 /* Tell the driver not to make a copy of the texture but to use
464 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
465 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
468 /* Use VRAM texturing */
469 glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
470 GL_STORAGE_CACHED_APPLE);
472 /* Use AGP texturing */
473 glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
474 GL_STORAGE_SHARED_APPLE);
478 /* Call glTexImage2D only once, and use glTexSubImage2D later */
479 glTexImage2D(vgl->tex_target, 0,
480 vgl->tex_format, vgl->tex_width[j], vgl->tex_height[j],
481 0, vgl->tex_format, vgl->tex_type, NULL);
485 vlc_gl_Unlock(vgl->gl);
490 for (unsigned i = 0; i < count; i++)
491 picture_Delete(picture[i]);
495 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
496 picture_t *picture, subpicture_t *subpicture)
498 /* On Win32/GLX, we do this the usual way:
499 + Fill the buffer with new content,
500 + Reload the texture,
503 On OS X with VRAM or AGP texturing, the order has to be:
504 + Reload the texture,
505 + Fill the buffer with new content,
508 (Thanks to gcc from the Arstechnica forums for the tip)
510 Therefore on OSX, we have to use two buffers and textures and use a
511 lock(/unlock) managed picture pool.
514 if (vlc_gl_Lock(vgl->gl))
518 /* Bind to the texture for drawing */
519 glBindTexture(vgl->tex_target, PictureGetTexture(picture));
521 /* Update the texture */
522 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
523 if (vgl->chroma->plane_count > 1)
524 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
525 glBindTexture(vgl->tex_target, vgl->texture[0][j]);
526 glPixelStorei(GL_UNPACK_ROW_LENGTH, picture->p[j].i_pitch / picture->p[j].i_pixel_pitch);
527 glTexSubImage2D(vgl->tex_target, 0,
529 vgl->fmt.i_width * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den,
530 vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den,
531 vgl->tex_format, vgl->tex_type, picture->p[j].p_pixels);
535 vlc_gl_Unlock(vgl->gl);
536 VLC_UNUSED(subpicture);
540 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
541 const video_format_t *source)
543 if (vlc_gl_Lock(vgl->gl))
546 /* glTexCoord works differently with GL_TEXTURE_2D and
547 GL_TEXTURE_RECTANGLE_EXT */
548 float left[PICTURE_PLANE_MAX];
549 float top[PICTURE_PLANE_MAX];
550 float right[PICTURE_PLANE_MAX];
551 float bottom[PICTURE_PLANE_MAX];
552 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
553 float scale_w, scale_h;
554 if (vgl->tex_target == GL_TEXTURE_2D) {
555 scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
556 scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];
562 left[j] = (source->i_x_offset + 0 ) * scale_w;
563 top[j] = (source->i_y_offset + 0 ) * scale_h;
564 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
565 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
569 /* Why drawing here and not in Render()? Because this way, the
570 OpenGL providers can call vout_display_opengl_Display to force redraw.i
571 Currently, the OS X provider uses it to get a smooth window resizing */
573 glClear(GL_COLOR_BUFFER_BIT);
576 glEnable(GL_FRAGMENT_PROGRAM_ARB);
577 for (int i = 0; i < vgl->local_count; i++)
578 vgl->ProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, i, vgl->local_value[i]);
580 glEnable(vgl->tex_target);
584 static const GLfloat vertexCoord[] = {
591 const GLfloat textureCoord[8] = {
598 glEnableClientState(GL_VERTEX_ARRAY);
599 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
600 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
601 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
603 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
605 #if !defined(MACOS_OPENGL)
606 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
607 if (vgl->chroma->plane_count > 1)
608 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
609 glBindTexture(vgl->tex_target, vgl->texture[0][j]);
614 glTexCoord2f(left[0], top[0]);
615 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
616 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], top[j]);
617 glVertex2f(-1.0, 1.0);
619 glTexCoord2f(right[0], top[0]);
620 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
621 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], top[j]);
622 glVertex2f( 1.0, 1.0);
624 glTexCoord2f(right[0], bottom[0]);
625 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
626 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], bottom[j]);
627 glVertex2f( 1.0, -1.0);
629 glTexCoord2f(left[0], bottom[0]);
630 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
631 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], bottom[j]);
632 glVertex2f(-1.0, -1.0);
638 glDisable(GL_FRAGMENT_PROGRAM_ARB);
640 glDisable(vgl->tex_target);
642 vlc_gl_Swap(vgl->gl);
644 vlc_gl_Unlock(vgl->gl);