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 /* fragment_program */
96 void (*GenProgramsARB)(GLuint, GLuint *);
97 void (*BindProgramARB)(GLuint, GLuint);
98 void (*ProgramStringARB)(GLuint, GLuint, GLint, const GLbyte *);
99 void (*DeleteProgramsARB)(GLuint, GLuint *);
102 void (*ActiveTextureARB)(GLuint);
103 void (*MultiTexCoord2fARB)(GLenum, GLfloat, GLfloat);
106 static inline int GetAlignedSize(unsigned size)
108 /* Return the smallest larger or equal power of 2 */
109 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
110 return ((align >> 1) == size) ? size : align;
113 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
116 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
121 if (vlc_gl_Lock(vgl->gl)) {
126 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
130 /* Load extensions */
131 bool supports_fp = false;
132 if (strstr(extensions, "GL_ARB_fragment_program")) {
133 vgl->GenProgramsARB = (void (*)(GLuint, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glGenProgramsARB");
134 vgl->BindProgramARB = (void (*)(GLuint, GLuint))vlc_gl_GetProcAddress(vgl->gl, "glBindProgramARB");
135 vgl->ProgramStringARB = (void (*)(GLuint, GLuint, GLint, const GLbyte *))vlc_gl_GetProcAddress(vgl->gl, "glProgramStringARB");
136 vgl->DeleteProgramsARB = (void (*)(GLuint, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgramsARB");
138 supports_fp = vgl->GenProgramsARB &&
139 vgl->BindProgramARB &&
140 vgl->ProgramStringARB &&
141 vgl->DeleteProgramsARB;
143 bool supports_multitexture = false;
144 if (strstr(extensions, "GL_ARB_multitexture")) {
145 vgl->ActiveTextureARB = (void (*)(GLuint))vlc_gl_GetProcAddress(vgl->gl, "glActiveTextureARB");
146 vgl->MultiTexCoord2fARB = (void (*)(GLenum, GLfloat, GLfloat))vlc_gl_GetProcAddress(vgl->gl, "glMultiTexCoord2fARB");
148 supports_multitexture = vgl->ActiveTextureARB &&
149 vgl->MultiTexCoord2fARB;
152 /* Initialize with default chroma */
155 vgl->fmt.i_chroma = VLC_CODEC_RGB16;
156 # if defined(WORDS_BIGENDIAN)
157 vgl->fmt.i_rmask = 0x001f;
158 vgl->fmt.i_gmask = 0x07e0;
159 vgl->fmt.i_bmask = 0xf800;
161 vgl->fmt.i_rmask = 0xf800;
162 vgl->fmt.i_gmask = 0x07e0;
163 vgl->fmt.i_bmask = 0x001f;
165 vgl->tex_target = GL_TEXTURE_2D;
166 vgl->tex_format = GL_RGB;
167 vgl->tex_type = GL_UNSIGNED_SHORT_5_6_5;
168 #elif defined(MACOS_OPENGL)
169 # if defined(WORDS_BIGENDIAN)
170 vgl->fmt.i_chroma = VLC_CODEC_YUYV;
172 vgl->fmt.i_chroma = VLC_CODEC_UYVY;
174 vgl->tex_target = GL_TEXTURE_RECTANGLE_EXT;
175 vgl->tex_format = GL_YCBCR_422_APPLE;
176 vgl->tex_type = GL_UNSIGNED_SHORT_8_8_APPLE;
178 vgl->fmt.i_chroma = VLC_CODEC_RGB32;
179 # if defined(WORDS_BIGENDIAN)
180 vgl->fmt.i_rmask = 0xff000000;
181 vgl->fmt.i_gmask = 0x00ff0000;
182 vgl->fmt.i_bmask = 0x0000ff00;
184 vgl->fmt.i_rmask = 0x000000ff;
185 vgl->fmt.i_gmask = 0x0000ff00;
186 vgl->fmt.i_bmask = 0x00ff0000;
188 vgl->tex_target = GL_TEXTURE_2D;
189 vgl->tex_format = GL_RGBA;
190 vgl->tex_type = GL_UNSIGNED_BYTE;
192 /* Use YUV if possible and needed */
193 bool need_fs_yuv = false;
194 if (supports_fp && supports_multitexture &&
195 vlc_fourcc_IsYUV(fmt->i_chroma) && !vlc_fourcc_IsYUV(vgl->fmt.i_chroma)) {
196 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
198 const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
199 if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
202 vgl->fmt.i_chroma = *list;
203 vgl->tex_format = GL_LUMINANCE;
204 vgl->tex_type = GL_UNSIGNED_BYTE;
211 vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
213 bool supports_npot = false;
214 #if USE_OPENGL_ES == 2
215 supports_npot = true;
216 #elif defined(MACOS_OPENGL)
217 supports_npot = true;
219 supports_npot |= strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != NULL ||
220 strstr(extensions, "GL_ARB_texture_non_power_of_two");
224 * TODO calculate the size such that the pictures can be used as
227 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
228 int w = vgl->fmt.i_width * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
229 int h = vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
231 vgl->tex_width[j] = w;
232 vgl->tex_height[j] = h;
235 /* A texture must have a size aligned on a power of 2 */
236 vgl->tex_width[j] = GetAlignedSize(w);
237 vgl->tex_height[j] = GetAlignedSize(h);
241 /* Build fragment program if needed */
247 /* [R/G/B][Y U V O] from TV range to full range
248 * XXX we could also do hue/brightness/constrast/gamma
249 * by simply changing the coefficients
251 const float matrix_bt601_tv2full[3][4] = {
252 { 1.1640, 0.0000, 1.4030, -0.7773 },
253 { 1.1640, -0.3440, -0.7140, 0.4580 },
254 { 1.1640, 1.7730, 0.0000, -0.9630 },
256 const float matrix_bt709_tv2full[3][4] = {
257 { 1.1640, 0.0000, 1.5701, -0.8612 },
258 { 1.1640, -0.1870, -0.4664, 0.2549 },
259 { 1.1640, 1.8556, 0.0000, -1.0045 },
261 const float (*matrix)[4] = fmt->i_height > 576 ? matrix_bt709_tv2full
262 : matrix_bt601_tv2full;
264 /* Basic linear YUV -> RGB conversion using bilinear interpolation */
265 const char *template_yuv =
267 "OPTION ARB_precision_hint_fastest;"
270 "TEX src.x, fragment.texcoord[0], texture[0], 2D;"
271 "TEX src.%c, fragment.texcoord[1], texture[1], 2D;"
272 "TEX src.%c, fragment.texcoord[2], texture[2], 2D;"
274 "PARAM muly = { %f, %f, %f };"
275 "PARAM mulu = { %f, %f, %f };"
276 "PARAM mulv = { %f, %f, %f };"
277 "PARAM offset = { %f, %f, %f };"
280 "MAD tmp.rgb, src.xxxx, muly, offset;"
281 "MAD tmp.rgb, src.yyyy, mulu, tmp;"
282 "MAD result.color.rgb, src.zzzz, mulv, tmp;"
284 bool swap_uv = vgl->fmt.i_chroma == VLC_CODEC_YV12 ||
285 vgl->fmt.i_chroma == VLC_CODEC_YV9;
286 if (asprintf(&code, template_yuv,
289 matrix[0][0], matrix[1][0], matrix[2][0],
290 matrix[0][1], matrix[1][1], matrix[2][1],
291 matrix[0][2], matrix[1][2], matrix[2][2],
292 matrix[0][3], matrix[1][3], matrix[2][3]) < 0)
296 vgl->GenProgramsARB(1, &vgl->program);
297 vgl->BindProgramARB(GL_FRAGMENT_PROGRAM_ARB, vgl->program);
298 vgl->ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
299 GL_PROGRAM_FORMAT_ASCII_ARB,
300 strlen(code), (const GLbyte*)code);
301 if (glGetError() == GL_INVALID_OPERATION) {
302 /* FIXME if the program was needed for YUV, the video will be broken */
305 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position);
307 const char *msg = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
308 fprintf(stderr, "GL_INVALID_OPERATION: error at %d: %s\n", position, msg);
310 vgl->DeleteProgramsARB(1, &vgl->program);
319 glDisable(GL_DEPTH_TEST);
320 glDepthMask(GL_FALSE);
321 glDisable(GL_CULL_FACE);
322 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
323 glClear(GL_COLOR_BUFFER_BIT);
325 vlc_gl_Unlock(vgl->gl);
328 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
329 for (int j = 0; j < PICTURE_PLANE_MAX; j++)
330 vgl->texture[i][j] = 0;
338 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
341 if (!vlc_gl_Lock(vgl->gl)) {
345 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
346 glDeleteTextures(vgl->chroma->plane_count, vgl->texture[i]);
349 vgl->DeleteProgramsARB(1, &vgl->program);
351 vlc_gl_Unlock(vgl->gl);
354 picture_pool_Delete(vgl->pool);
359 struct picture_sys_t {
360 vout_display_opengl_t *vgl;
365 static inline GLuint PictureGetTexture(picture_t *picture)
367 return *picture->p_sys->texture;
370 static int PictureLock(picture_t *picture)
375 vout_display_opengl_t *vgl = picture->p_sys->vgl;
376 if (!vlc_gl_Lock(vgl->gl)) {
377 glBindTexture(vgl->tex_target, PictureGetTexture(picture));
378 glTexSubImage2D(vgl->tex_target, 0,
379 0, 0, vgl->fmt.i_width, vgl->fmt.i_height,
380 vgl->tex_format, vgl->tex_type, picture->p[0].p_pixels);
382 vlc_gl_Unlock(vgl->gl);
387 static void PictureUnlock(picture_t *picture)
393 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
398 /* Allocate our pictures */
399 picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
402 for (count = 0; count < __MIN(VLCGL_PICTURE_MAX, requested_count); count++) {
403 picture[count] = picture_NewFromFormat(&vgl->fmt);
408 picture_sys_t *sys = picture[count]->p_sys = malloc(sizeof(*sys));
411 sys->texture = vgl->texture[count];
418 /* Wrap the pictures into a pool */
419 picture_pool_configuration_t cfg;
420 memset(&cfg, 0, sizeof(cfg));
421 cfg.picture_count = count;
422 cfg.picture = picture;
424 cfg.lock = PictureLock;
425 cfg.unlock = PictureUnlock;
427 vgl->pool = picture_pool_NewExtended(&cfg);
431 /* Allocates our textures */
432 if (vlc_gl_Lock(vgl->gl))
435 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
436 glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
437 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
438 if (vgl->chroma->plane_count > 1)
439 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
440 glBindTexture(vgl->tex_target, vgl->texture[i][j]);
443 /* Set the texture parameters */
444 glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
445 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
448 glTexParameteri(vgl->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
449 glTexParameteri(vgl->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
450 glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
451 glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
454 /* Tell the driver not to make a copy of the texture but to use
456 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
457 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
460 /* Use VRAM texturing */
461 glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
462 GL_STORAGE_CACHED_APPLE);
464 /* Use AGP texturing */
465 glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
466 GL_STORAGE_SHARED_APPLE);
470 /* Call glTexImage2D only once, and use glTexSubImage2D later */
471 glTexImage2D(vgl->tex_target, 0,
472 vgl->tex_format, vgl->tex_width[j], vgl->tex_height[j],
473 0, vgl->tex_format, vgl->tex_type, NULL);
477 vlc_gl_Unlock(vgl->gl);
482 for (unsigned i = 0; i < count; i++)
483 picture_Delete(picture[i]);
487 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
490 /* On Win32/GLX, we do this the usual way:
491 + Fill the buffer with new content,
492 + Reload the texture,
495 On OS X with VRAM or AGP texturing, the order has to be:
496 + Reload the texture,
497 + Fill the buffer with new content,
500 (Thanks to gcc from the Arstechnica forums for the tip)
502 Therefore on OSX, we have to use two buffers and textures and use a
503 lock(/unlock) managed picture pool.
506 if (vlc_gl_Lock(vgl->gl))
510 /* Bind to the texture for drawing */
511 glBindTexture(vgl->tex_target, PictureGetTexture(picture));
513 /* Update the texture */
514 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
515 if (vgl->chroma->plane_count > 1)
516 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
517 glPixelStorei(GL_UNPACK_ROW_LENGTH, picture->p[j].i_pitch / picture->p[j].i_pixel_pitch);
518 glTexSubImage2D(vgl->tex_target, 0,
520 vgl->fmt.i_width * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den,
521 vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den,
522 vgl->tex_format, vgl->tex_type, picture->p[j].p_pixels);
526 vlc_gl_Unlock(vgl->gl);
530 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
531 const video_format_t *source)
533 if (vlc_gl_Lock(vgl->gl))
536 /* glTexCoord works differently with GL_TEXTURE_2D and
537 GL_TEXTURE_RECTANGLE_EXT */
538 float left[PICTURE_PLANE_MAX];
539 float top[PICTURE_PLANE_MAX];
540 float right[PICTURE_PLANE_MAX];
541 float bottom[PICTURE_PLANE_MAX];
542 for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
543 float scale_w, scale_h;
544 if (vgl->tex_target == GL_TEXTURE_2D) {
545 scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
546 scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];
552 left[j] = (source->i_x_offset + 0 ) * scale_w;
553 top[j] = (source->i_y_offset + 0 ) * scale_h;
554 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
555 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
559 /* Why drawing here and not in Render()? Because this way, the
560 OpenGL providers can call vout_display_opengl_Display to force redraw.i
561 Currently, the OS X provider uses it to get a smooth window resizing */
563 glClear(GL_COLOR_BUFFER_BIT);
566 glEnable(GL_FRAGMENT_PROGRAM_ARB);
568 glEnable(vgl->tex_target);
571 static const GLfloat vertexCoord[] = {
578 const GLfloat textureCoord[8] = {
585 glEnableClientState(GL_VERTEX_ARRAY);
586 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
587 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
588 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
590 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
594 glTexCoord2f(left[0], top[0]);
595 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
596 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], top[j]);
597 glVertex2f(-1.0, 1.0);
599 glTexCoord2f(right[0], top[0]);
600 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
601 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], top[j]);
602 glVertex2f( 1.0, 1.0);
604 glTexCoord2f(right[0], bottom[0]);
605 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
606 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], bottom[j]);
607 glVertex2f( 1.0, -1.0);
609 glTexCoord2f(left[0], bottom[0]);
610 for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
611 vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], bottom[j]);
612 glVertex2f(-1.0, -1.0);
618 glDisable(GL_FRAGMENT_PROGRAM_ARB);
620 glDisable(vgl->tex_target);
622 vlc_gl_Swap(vgl->gl);
624 vlc_gl_Unlock(vgl->gl);