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 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_picture_pool.h>
30 #include <vlc_opengl.h>
33 // Define USE_OPENGL_ES to the GL ES Version you want to select
35 #if !defined (__APPLE__)
36 # if USE_OPENGL_ES == 2
37 # include <GLES2/gl2ext.h>
38 # elif USE_OPENGL_ES == 1
39 # include <GLES/glext.h>
41 //# include <GL/glext.h>
44 # if USE_OPENGL_ES == 2
45 # include <OpenGLES/ES2/gl.h>
46 # elif USE_OPENGL_ES == 1
47 # include <OpenGLES/ES1/gl.h>
50 # include <OpenGL/glext.h>
55 #ifndef GL_UNSIGNED_SHORT_5_6_5
56 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
58 #ifndef GL_CLAMP_TO_EDGE
59 # define GL_CLAMP_TO_EDGE 0x812F
64 # define VLCGL_TARGET GL_TEXTURE_2D
66 // Use RGB with OpenGLES
67 # define VLCGL_FORMAT GL_RGB
68 # define VLCGL_TYPE GL_UNSIGNED_SHORT_5_6_5
70 # define VLCGL_TEXTURE_COUNT 1
72 #elif defined(MACOS_OPENGL)
74 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
75 This allows sizes which are not powers of 2 */
76 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
78 /* OS X OpenGL supports YUV. Hehe. */
79 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
80 # define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
82 # define VLCGL_TEXTURE_COUNT 2
86 # define VLCGL_TARGET GL_TEXTURE_2D
88 /* Use RGB on Win32/GLX */
89 # define VLCGL_FORMAT GL_RGBA
90 # define VLCGL_TYPE GL_UNSIGNED_BYTE
92 # define VLCGL_TEXTURE_COUNT 1
95 struct vout_display_opengl_t {
99 const vlc_chroma_description_t *chroma;
104 GLuint texture[VLCGL_TEXTURE_COUNT];
105 uint8_t *buffer[VLCGL_TEXTURE_COUNT];
106 void *buffer_base[VLCGL_TEXTURE_COUNT];
108 picture_pool_t *pool;
111 static inline int GetAlignedSize(unsigned size)
113 /* Return the smallest larger or equal power of 2 */
114 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
115 return ((align >> 1) == size) ? size : align;
118 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
121 vout_display_opengl_t *vgl = malloc(sizeof(*vgl));
126 if (vlc_gl_Lock(vgl->gl)) {
131 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
135 /* Find the chroma we will use and update fmt */
137 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
138 #if defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE
139 vgl->fmt.i_chroma = VLC_CODEC_YUYV;
140 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
141 vgl->fmt.i_chroma = VLC_CODEC_UYVY;
142 #elif VLCGL_FORMAT == GL_RGB
143 # if VLCGL_TYPE == GL_UNSIGNED_BYTE
144 vgl->fmt.i_chroma = VLC_CODEC_RGB24;
145 # if defined(WORDS_BIGENDIAN)
146 vgl->fmt.i_rmask = 0x00ff0000;
147 vgl->fmt.i_gmask = 0x0000ff00;
148 vgl->fmt.i_bmask = 0x000000ff;
150 vgl->fmt.i_rmask = 0x000000ff;
151 vgl->fmt.i_gmask = 0x0000ff00;
152 vgl->fmt.i_bmask = 0x00ff0000;
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;
167 vgl->fmt.i_chroma = VLC_CODEC_RGB32;
168 # if defined(WORDS_BIGENDIAN)
169 vgl->fmt.i_rmask = 0xff000000;
170 vgl->fmt.i_gmask = 0x00ff0000;
171 vgl->fmt.i_bmask = 0x0000ff00;
173 vgl->fmt.i_rmask = 0x000000ff;
174 vgl->fmt.i_gmask = 0x0000ff00;
175 vgl->fmt.i_bmask = 0x00ff0000;
179 vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
181 bool supports_npot = false;
182 #if USE_OPENGL_ES == 2
183 supports_npot = true;
184 #elif defined(MACOS_OPENGL)
185 supports_npot = true;
187 supports_npot |= strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != NULL ||
188 strstr(extensions, "GL_ARB_texture_non_power_of_two");
193 vgl->tex_width = vgl->fmt.i_width;
194 vgl->tex_height = vgl->fmt.i_height;
197 /* A texture must have a size aligned on a power of 2 */
198 vgl->tex_width = GetAlignedSize(vgl->fmt.i_width);
199 vgl->tex_height = GetAlignedSize(vgl->fmt.i_height);
204 glDisable(GL_DEPTH_TEST);
205 glDepthMask(GL_FALSE);
206 glDisable(GL_CULL_FACE);
207 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
208 glClear(GL_COLOR_BUFFER_BIT);
210 vlc_gl_Unlock(vgl->gl);
213 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
215 vgl->buffer[i] = NULL;
216 vgl->buffer_base[i] = NULL;
224 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
227 if (!vlc_gl_Lock(vgl->gl)) {
231 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
233 vlc_gl_Unlock(vgl->gl);
236 picture_pool_Delete(vgl->pool);
237 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
238 free(vgl->buffer_base[i]);
244 /* XXX See comment vout_display_opengl_Prepare */
245 struct picture_sys_t {
246 vout_display_opengl_t *vgl;
251 static inline GLuint get_texture(picture_t *picture)
253 return *picture->p_sys->texture;
256 static int PictureLock(picture_t *picture)
261 vout_display_opengl_t *vgl = picture->p_sys->vgl;
262 if (!vlc_gl_Lock(vgl->gl)) {
263 glBindTexture(VLCGL_TARGET, get_texture(picture));
264 glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
265 vgl->fmt.i_width, vgl->fmt.i_height,
266 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
268 vlc_gl_Unlock(vgl->gl);
273 static void PictureUnlock(picture_t *picture)
279 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
284 picture_t *picture[VLCGL_TEXTURE_COUNT];
287 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
288 vgl->buffer[i] = vlc_memalign(&vgl->buffer_base[i], 16,
289 vgl->tex_width * vgl->tex_height * vgl->chroma->pixel_size);
293 picture_resource_t rsc;
294 memset(&rsc, 0, sizeof(rsc));
296 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
299 rsc.p_sys->vgl = vgl;
300 rsc.p_sys->texture = &vgl->texture[i];
303 rsc.p[0].p_pixels = vgl->buffer[i];
304 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->chroma->pixel_size;
305 rsc.p[0].i_lines = vgl->fmt.i_height;
307 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
309 free(vgl->buffer[i]);
310 vgl->buffer[i] = NULL;
314 if (i < VLCGL_TEXTURE_COUNT)
318 picture_pool_configuration_t cfg;
319 memset(&cfg, 0, sizeof(cfg));
320 cfg.picture_count = i;
321 cfg.picture = picture;
323 cfg.lock = PictureLock;
324 cfg.unlock = PictureUnlock;
326 vgl->pool = picture_pool_NewExtended(&cfg);
330 if (vlc_gl_Lock(vgl->gl))
333 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
334 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
335 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
338 /* Set the texture parameters */
339 glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
340 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
343 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
344 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
345 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
346 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
349 /* Tell the driver not to make a copy of the texture but to use
351 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
352 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
355 /* Use VRAM texturing */
356 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
357 GL_STORAGE_CACHED_APPLE);
359 /* Use AGP texturing */
360 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
361 GL_STORAGE_SHARED_APPLE);
365 /* Call glTexImage2D only once, and use glTexSubImage2D later */
366 if (vgl->buffer[i]) {
367 glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
368 vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
373 vlc_gl_Unlock(vgl->gl);
378 for (int j = 0; j < i; j++) {
379 picture_Delete(picture[j]);
380 vgl->buffer[j] = NULL;
385 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
388 /* On Win32/GLX, we do this the usual way:
389 + Fill the buffer with new content,
390 + Reload the texture,
393 On OS X with VRAM or AGP texturing, the order has to be:
394 + Reload the texture,
395 + Fill the buffer with new content,
398 (Thanks to gcc from the Arstechnica forums for the tip)
400 Therefore on OSX, we have to use two buffers and textures and use a
401 lock(/unlock) managed picture pool.
404 if (vlc_gl_Lock(vgl->gl))
408 /* Bind to the texture for drawing */
409 glBindTexture(VLCGL_TARGET, get_texture(picture));
411 /* Update the texture */
412 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
413 vgl->fmt.i_width, vgl->fmt.i_height,
414 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
417 vlc_gl_Unlock(vgl->gl);
421 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
422 const video_format_t *source)
424 if (vlc_gl_Lock(vgl->gl))
427 /* glTexCoord works differently with GL_TEXTURE_2D and
428 GL_TEXTURE_RECTANGLE_EXT */
429 #if VLCGL_TARGET == GL_TEXTURE_2D
430 const float f_normw = vgl->tex_width;
431 const float f_normh = vgl->tex_height;
432 #elif defined (GL_TEXTURE_RECTANGLE_EXT) \
433 && (VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT)
434 const float f_normw = 1.0;
435 const float f_normh = 1.0;
437 # error Unknown texture type!
440 float f_x = (source->i_x_offset + 0 ) / f_normw;
441 float f_y = (source->i_y_offset + 0 ) / f_normh;
442 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
443 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
445 /* Why drawing here and not in Render()? Because this way, the
446 OpenGL providers can call vout_display_opengl_Display to force redraw.i
447 Currently, the OS X provider uses it to get a smooth window resizing */
449 glClear(GL_COLOR_BUFFER_BIT);
451 glEnable(VLCGL_TARGET);
454 static const GLfloat vertexCoord[] = {
461 const GLfloat textureCoord[8] = {
468 glEnableClientState(GL_VERTEX_ARRAY);
469 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
470 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
471 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
473 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
476 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
477 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
478 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
479 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
483 glDisable(VLCGL_TARGET);
485 vlc_gl_Swap(vgl->gl);
487 vlc_gl_Unlock(vgl->gl);