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
67 # define VLCGL_TARGET GL_TEXTURE_2D
69 // Use RGB with OpenGLES
70 # define VLCGL_FORMAT GL_RGB
71 # define VLCGL_TYPE GL_UNSIGNED_SHORT_5_6_5
73 # define VLCGL_TEXTURE_COUNT 1
75 #elif defined(MACOS_OPENGL)
77 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
78 This allows sizes which are not powers of 2 */
79 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
81 /* OS X OpenGL supports YUV. Hehe. */
82 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
83 # define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
85 # define VLCGL_TEXTURE_COUNT 2
89 # define VLCGL_TARGET GL_TEXTURE_2D
91 /* Use RGB on Win32/GLX */
92 # define VLCGL_FORMAT GL_RGBA
93 # define VLCGL_TYPE GL_UNSIGNED_BYTE
95 # define VLCGL_TEXTURE_COUNT 1
98 struct vout_display_opengl_t {
102 const vlc_chroma_description_t *chroma;
107 GLuint texture[VLCGL_TEXTURE_COUNT];
108 uint8_t *buffer[VLCGL_TEXTURE_COUNT];
109 void *buffer_base[VLCGL_TEXTURE_COUNT];
111 picture_pool_t *pool;
115 /* fragment_program */
116 void (*GenProgramsARB)(GLuint, GLuint *);
117 void (*BindProgramARB)(GLuint, GLuint);
118 void (*ProgramStringARB)(GLuint, GLuint, GLint, const GLbyte *);
119 void (*DeleteProgramsARB)(GLuint, GLuint *);
122 static inline int GetAlignedSize(unsigned size)
124 /* Return the smallest larger or equal power of 2 */
125 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
126 return ((align >> 1) == size) ? size : align;
129 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
132 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
137 if (vlc_gl_Lock(vgl->gl)) {
142 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
146 /* Load extensions */
147 bool supports_fp = false;
148 if (strstr(extensions, "GL_ARB_fragment_program")) {
149 vgl->GenProgramsARB = (void (*)(GLuint, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glGenProgramsARB");
150 vgl->BindProgramARB = (void (*)(GLuint, GLuint))vlc_gl_GetProcAddress(vgl->gl, "glBindProgramARB");
151 vgl->ProgramStringARB = (void (*)(GLuint, GLuint, GLint, const GLbyte *))vlc_gl_GetProcAddress(vgl->gl, "glProgramStringARB");
152 vgl->DeleteProgramsARB = (void (*)(GLuint, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgramsARB");
154 supports_fp = vgl->GenProgramsARB &&
155 vgl->BindProgramARB &&
156 vgl->ProgramStringARB &&
157 vgl->DeleteProgramsARB;
160 /* Find the chroma we will use and update fmt */
162 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
163 #if defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE
164 vgl->fmt.i_chroma = VLC_CODEC_YUYV;
165 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
166 vgl->fmt.i_chroma = VLC_CODEC_UYVY;
167 #elif VLCGL_FORMAT == GL_RGB
168 # if VLCGL_TYPE == GL_UNSIGNED_BYTE
169 vgl->fmt.i_chroma = VLC_CODEC_RGB24;
170 # if defined(WORDS_BIGENDIAN)
171 vgl->fmt.i_rmask = 0x00ff0000;
172 vgl->fmt.i_gmask = 0x0000ff00;
173 vgl->fmt.i_bmask = 0x000000ff;
175 vgl->fmt.i_rmask = 0x000000ff;
176 vgl->fmt.i_gmask = 0x0000ff00;
177 vgl->fmt.i_bmask = 0x00ff0000;
180 vgl->fmt.i_chroma = VLC_CODEC_RGB16;
181 # if defined(WORDS_BIGENDIAN)
182 vgl->fmt.i_rmask = 0x001f;
183 vgl->fmt.i_gmask = 0x07e0;
184 vgl->fmt.i_bmask = 0xf800;
186 vgl->fmt.i_rmask = 0xf800;
187 vgl->fmt.i_gmask = 0x07e0;
188 vgl->fmt.i_bmask = 0x001f;
192 vgl->fmt.i_chroma = VLC_CODEC_RGB32;
193 # if defined(WORDS_BIGENDIAN)
194 vgl->fmt.i_rmask = 0xff000000;
195 vgl->fmt.i_gmask = 0x00ff0000;
196 vgl->fmt.i_bmask = 0x0000ff00;
198 vgl->fmt.i_rmask = 0x000000ff;
199 vgl->fmt.i_gmask = 0x0000ff00;
200 vgl->fmt.i_bmask = 0x00ff0000;
204 vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
206 bool supports_npot = false;
207 #if USE_OPENGL_ES == 2
208 supports_npot = true;
209 #elif defined(MACOS_OPENGL)
210 supports_npot = true;
212 supports_npot |= strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != NULL ||
213 strstr(extensions, "GL_ARB_texture_non_power_of_two");
218 vgl->tex_width = vgl->fmt.i_width;
219 vgl->tex_height = vgl->fmt.i_height;
222 /* A texture must have a size aligned on a power of 2 */
223 vgl->tex_width = GetAlignedSize(vgl->fmt.i_width);
224 vgl->tex_height = GetAlignedSize(vgl->fmt.i_height);
227 /* Build fragment program if needed */
232 vgl->GenProgramsARB(1, &vgl->program);
233 vgl->BindProgramARB(GL_FRAGMENT_PROGRAM_ARB, vgl->program);
234 vgl->ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
235 GL_PROGRAM_FORMAT_ASCII_ARB,
236 strlen(code), (const GLbyte*)code);
237 if (glGetError() == GL_INVALID_OPERATION) {
238 /* FIXME if the program was needed for YUV, the video will be broken */
241 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position);
243 const char *msg = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
244 fprintf(stderr, "GL_INVALID_OPERATION: error at %d: %s\n", position, msg);
246 vgl->DeleteProgramsARB(1, &vgl->program);
255 glDisable(GL_DEPTH_TEST);
256 glDepthMask(GL_FALSE);
257 glDisable(GL_CULL_FACE);
258 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
259 glClear(GL_COLOR_BUFFER_BIT);
261 vlc_gl_Unlock(vgl->gl);
264 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
266 vgl->buffer[i] = NULL;
267 vgl->buffer_base[i] = NULL;
275 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
278 if (!vlc_gl_Lock(vgl->gl)) {
282 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
285 vgl->DeleteProgramsARB(1, &vgl->program);
287 vlc_gl_Unlock(vgl->gl);
290 picture_pool_Delete(vgl->pool);
291 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
292 free(vgl->buffer_base[i]);
298 /* XXX See comment vout_display_opengl_Prepare */
299 struct picture_sys_t {
300 vout_display_opengl_t *vgl;
305 static inline GLuint get_texture(picture_t *picture)
307 return *picture->p_sys->texture;
310 static int PictureLock(picture_t *picture)
315 vout_display_opengl_t *vgl = picture->p_sys->vgl;
316 if (!vlc_gl_Lock(vgl->gl)) {
317 glBindTexture(VLCGL_TARGET, get_texture(picture));
318 glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
319 picture->p[0].i_pitch / vgl->chroma->pixel_size,
320 picture->p[0].i_lines,
321 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
323 vlc_gl_Unlock(vgl->gl);
328 static void PictureUnlock(picture_t *picture)
334 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
339 picture_t *picture[VLCGL_TEXTURE_COUNT];
342 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
343 vgl->buffer[i] = vlc_memalign(&vgl->buffer_base[i], 16,
344 vgl->tex_width * vgl->tex_height * vgl->chroma->pixel_size);
348 picture_resource_t rsc;
349 memset(&rsc, 0, sizeof(rsc));
351 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
354 rsc.p_sys->vgl = vgl;
355 rsc.p_sys->texture = &vgl->texture[i];
358 rsc.p[0].p_pixels = vgl->buffer[i];
359 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->chroma->pixel_size;
360 rsc.p[0].i_lines = vgl->fmt.i_height;
362 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
364 free(vgl->buffer[i]);
365 vgl->buffer[i] = NULL;
369 if (i < VLCGL_TEXTURE_COUNT)
373 picture_pool_configuration_t cfg;
374 memset(&cfg, 0, sizeof(cfg));
375 cfg.picture_count = i;
376 cfg.picture = picture;
378 cfg.lock = PictureLock;
379 cfg.unlock = PictureUnlock;
381 vgl->pool = picture_pool_NewExtended(&cfg);
385 if (vlc_gl_Lock(vgl->gl))
388 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
389 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
390 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
393 /* Set the texture parameters */
394 glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
395 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
398 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
399 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
400 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
401 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
404 /* Tell the driver not to make a copy of the texture but to use
406 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
407 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
410 /* Use VRAM texturing */
411 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
412 GL_STORAGE_CACHED_APPLE);
414 /* Use AGP texturing */
415 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
416 GL_STORAGE_SHARED_APPLE);
420 /* Call glTexImage2D only once, and use glTexSubImage2D later */
421 if (vgl->buffer[i]) {
422 glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
423 vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
428 vlc_gl_Unlock(vgl->gl);
433 for (int j = 0; j < i; j++) {
434 picture_Delete(picture[j]);
435 vgl->buffer[j] = NULL;
440 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
443 /* On Win32/GLX, we do this the usual way:
444 + Fill the buffer with new content,
445 + Reload the texture,
448 On OS X with VRAM or AGP texturing, the order has to be:
449 + Reload the texture,
450 + Fill the buffer with new content,
453 (Thanks to gcc from the Arstechnica forums for the tip)
455 Therefore on OSX, we have to use two buffers and textures and use a
456 lock(/unlock) managed picture pool.
459 if (vlc_gl_Lock(vgl->gl))
463 /* Bind to the texture for drawing */
464 glBindTexture(VLCGL_TARGET, get_texture(picture));
466 /* Update the texture */
467 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
468 picture->p[0].i_pitch / vgl->chroma->pixel_size,
469 picture->p[0].i_lines,
470 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
473 vlc_gl_Unlock(vgl->gl);
477 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
478 const video_format_t *source)
480 if (vlc_gl_Lock(vgl->gl))
483 /* glTexCoord works differently with GL_TEXTURE_2D and
484 GL_TEXTURE_RECTANGLE_EXT */
485 #if VLCGL_TARGET == GL_TEXTURE_2D
486 const float f_normw = vgl->tex_width;
487 const float f_normh = vgl->tex_height;
488 #elif defined (GL_TEXTURE_RECTANGLE_EXT) \
489 && (VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT)
490 const float f_normw = 1.0;
491 const float f_normh = 1.0;
493 # error Unknown texture type!
496 float f_x = (source->i_x_offset + 0 ) / f_normw;
497 float f_y = (source->i_y_offset + 0 ) / f_normh;
498 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
499 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
501 /* Why drawing here and not in Render()? Because this way, the
502 OpenGL providers can call vout_display_opengl_Display to force redraw.i
503 Currently, the OS X provider uses it to get a smooth window resizing */
505 glClear(GL_COLOR_BUFFER_BIT);
508 glEnable(GL_FRAGMENT_PROGRAM_ARB);
510 glEnable(VLCGL_TARGET);
513 static const GLfloat vertexCoord[] = {
520 const GLfloat textureCoord[8] = {
527 glEnableClientState(GL_VERTEX_ARRAY);
528 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
529 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
530 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
532 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
535 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
536 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
537 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
538 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
543 glDisable(GL_FRAGMENT_PROGRAM_ARB);
545 glDisable(VLCGL_TARGET);
547 vlc_gl_Swap(vgl->gl);
549 vlc_gl_Unlock(vgl->gl);