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 picture->p[0].i_pitch / vgl->chroma->pixel_size,
266 picture->p[0].i_lines,
267 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
269 vlc_gl_Unlock(vgl->gl);
274 static void PictureUnlock(picture_t *picture)
280 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
285 picture_t *picture[VLCGL_TEXTURE_COUNT];
288 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
289 vgl->buffer[i] = vlc_memalign(&vgl->buffer_base[i], 16,
290 vgl->tex_width * vgl->tex_height * vgl->chroma->pixel_size);
294 picture_resource_t rsc;
295 memset(&rsc, 0, sizeof(rsc));
297 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
300 rsc.p_sys->vgl = vgl;
301 rsc.p_sys->texture = &vgl->texture[i];
304 rsc.p[0].p_pixels = vgl->buffer[i];
305 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->chroma->pixel_size;
306 rsc.p[0].i_lines = vgl->fmt.i_height;
308 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
310 free(vgl->buffer[i]);
311 vgl->buffer[i] = NULL;
315 if (i < VLCGL_TEXTURE_COUNT)
319 picture_pool_configuration_t cfg;
320 memset(&cfg, 0, sizeof(cfg));
321 cfg.picture_count = i;
322 cfg.picture = picture;
324 cfg.lock = PictureLock;
325 cfg.unlock = PictureUnlock;
327 vgl->pool = picture_pool_NewExtended(&cfg);
331 if (vlc_gl_Lock(vgl->gl))
334 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
335 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
336 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
339 /* Set the texture parameters */
340 glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
341 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
344 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
345 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
346 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
347 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
350 /* Tell the driver not to make a copy of the texture but to use
352 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
353 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
356 /* Use VRAM texturing */
357 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
358 GL_STORAGE_CACHED_APPLE);
360 /* Use AGP texturing */
361 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
362 GL_STORAGE_SHARED_APPLE);
366 /* Call glTexImage2D only once, and use glTexSubImage2D later */
367 if (vgl->buffer[i]) {
368 glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
369 vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
374 vlc_gl_Unlock(vgl->gl);
379 for (int j = 0; j < i; j++) {
380 picture_Delete(picture[j]);
381 vgl->buffer[j] = NULL;
386 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
389 /* On Win32/GLX, we do this the usual way:
390 + Fill the buffer with new content,
391 + Reload the texture,
394 On OS X with VRAM or AGP texturing, the order has to be:
395 + Reload the texture,
396 + Fill the buffer with new content,
399 (Thanks to gcc from the Arstechnica forums for the tip)
401 Therefore on OSX, we have to use two buffers and textures and use a
402 lock(/unlock) managed picture pool.
405 if (vlc_gl_Lock(vgl->gl))
409 /* Bind to the texture for drawing */
410 glBindTexture(VLCGL_TARGET, get_texture(picture));
412 /* Update the texture */
413 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
414 picture->p[0].i_pitch / vgl->chroma->pixel_size,
415 picture->p[0].i_lines,
416 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
419 vlc_gl_Unlock(vgl->gl);
423 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
424 const video_format_t *source)
426 if (vlc_gl_Lock(vgl->gl))
429 /* glTexCoord works differently with GL_TEXTURE_2D and
430 GL_TEXTURE_RECTANGLE_EXT */
431 #if VLCGL_TARGET == GL_TEXTURE_2D
432 const float f_normw = vgl->tex_width;
433 const float f_normh = vgl->tex_height;
434 #elif defined (GL_TEXTURE_RECTANGLE_EXT) \
435 && (VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT)
436 const float f_normw = 1.0;
437 const float f_normh = 1.0;
439 # error Unknown texture type!
442 float f_x = (source->i_x_offset + 0 ) / f_normw;
443 float f_y = (source->i_y_offset + 0 ) / f_normh;
444 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
445 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
447 /* Why drawing here and not in Render()? Because this way, the
448 OpenGL providers can call vout_display_opengl_Display to force redraw.i
449 Currently, the OS X provider uses it to get a smooth window resizing */
451 glClear(GL_COLOR_BUFFER_BIT);
453 glEnable(VLCGL_TARGET);
456 static const GLfloat vertexCoord[] = {
463 const GLfloat textureCoord[8] = {
470 glEnableClientState(GL_VERTEX_ARRAY);
471 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
472 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
473 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
475 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
478 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
479 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
480 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
481 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
485 glDisable(VLCGL_TARGET);
487 vlc_gl_Swap(vgl->gl);
489 vlc_gl_Unlock(vgl->gl);