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_vout_opengl.h>
33 // Define USE_OPENGL_ES to the GL ES Version you want to select
35 #if USE_OPENGL_ES == 1
36 # include <OpenGLES/ES1/glext.h>
37 #elif USE_OPENGL_ES == 2
38 # include <OpenGLES/ES2/glext.h>
39 #elif defined(__APPLE__)
41 # include <OpenGL/glext.h>
45 # define YCBCR_MESA 0x8757
47 #ifndef UNSIGNED_SHORT_8_8_MESA
48 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
51 #ifndef GL_UNSIGNED_SHORT_5_6_5
52 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
54 #ifndef GL_CLAMP_TO_EDGE
55 # define GL_CLAMP_TO_EDGE 0x812F
60 # define VLCGL_TARGET GL_TEXTURE_2D
62 # define VLCGL_RGB_FORMAT GL_RGB
63 # define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
65 // Use RGB with OpenGLES
66 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
67 # define VLCGL_TYPE VLCGL_RGB_TYPE
69 #elif defined(MACOS_OPENGL)
71 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
72 This allows sizes which are not powers of 2 */
73 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
75 /* OS X OpenGL supports YUV. Hehe. */
76 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
77 # define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
81 # define VLCGL_TARGET GL_TEXTURE_2D
84 # define VLCGL_RGB_FORMAT GL_RGBA
85 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
88 # define VLCGL_YUV_FORMAT YCBCR_MESA
89 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
91 /* Use RGB on Win32/GLX */
92 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
93 # define VLCGL_TYPE VLCGL_RGB_TYPE
96 static inline int GetAlignedSize(unsigned size)
98 /* Return the smallest larger or equal power of 2 */
99 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
100 return ((align >> 1) == size) ? size : align;
103 int vout_display_opengl_Init(vout_display_opengl_t *vgl,
109 /* Find the chroma we will use and update fmt */
110 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
111 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
112 fmt->i_chroma = VLC_CODEC_YUYV;
113 vgl->tex_pixel_size = 2;
114 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
115 fmt->i_chroma = VLC_CODEC_UYVY;
116 vgl->tex_pixel_size = 2;
117 #elif VLCGL_FORMAT == GL_RGB
118 # if VLCGL_TYPE == GL_UNSIGNED_BYTE
119 fmt->i_chroma = VLC_CODEC_RGB24;
120 # if defined(WORDS_BIGENDIAN)
121 fmt->i_rmask = 0x00ff0000;
122 fmt->i_gmask = 0x0000ff00;
123 fmt->i_bmask = 0x000000ff;
125 fmt->i_rmask = 0x000000ff;
126 fmt->i_gmask = 0x0000ff00;
127 fmt->i_bmask = 0x00ff0000;
129 vgl->tex_pixel_size = 3;
131 fmt->i_chroma = VLC_CODEC_RGB16;
132 # if defined(WORDS_BIGENDIAN)
133 fmt->i_rmask = 0x001f;
134 fmt->i_gmask = 0x07e0;
135 fmt->i_bmask = 0xf800;
137 fmt->i_rmask = 0xf800;
138 fmt->i_gmask = 0x07e0;
139 fmt->i_bmask = 0x001f;
141 vgl->tex_pixel_size = 2;
144 fmt->i_chroma = VLC_CODEC_RGB32;
145 # if defined(WORDS_BIGENDIAN)
146 fmt->i_rmask = 0xff000000;
147 fmt->i_gmask = 0x00ff0000;
148 fmt->i_bmask = 0x0000ff00;
150 fmt->i_rmask = 0x000000ff;
151 fmt->i_gmask = 0x0000ff00;
152 fmt->i_bmask = 0x00ff0000;
154 vgl->tex_pixel_size = 4;
160 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
162 vgl->buffer[i] = NULL;
166 bool supports_npot = false;
167 #if USE_OPENGL_ES == 2
168 supports_npot = true;
169 #elif defined(MACOS_OPENGL)
170 supports_npot = true;
173 #if defined(__APPLE__) && USE_OPENGL_ES == 1
174 if (!vout_opengl_Lock(vgl->gl)) {
175 const char* extensions = (char*) glGetString(GL_EXTENSIONS);
177 bool npot = strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != 0;
179 supports_npot = true;
181 vout_opengl_Unlock(vgl->gl);
187 vgl->tex_width = fmt->i_width;
188 vgl->tex_height = fmt->i_height;
191 /* A texture must have a size aligned on a power of 2 */
192 vgl->tex_width = GetAlignedSize(fmt->i_width);
193 vgl->tex_height = GetAlignedSize(fmt->i_height);
198 if (!vout_opengl_Lock(vgl->gl)) {
201 glDisable(GL_DEPTH_TEST);
202 glDepthMask(GL_FALSE);
203 glDisable(GL_CULL_FACE);
204 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
205 glClear(GL_COLOR_BUFFER_BIT);
207 vout_opengl_Unlock(vgl->gl);
212 void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
215 if (!vout_opengl_Lock(vgl->gl)) {
219 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
221 vout_opengl_Unlock(vgl->gl);
224 picture_pool_Delete(vgl->pool);
225 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
226 free(vgl->buffer[i]);
230 int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
232 if (vout_opengl_Lock(vgl->gl))
235 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
237 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
238 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
239 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
242 /* Set the texture parameters */
243 glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
244 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
247 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
248 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
249 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
250 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
253 /* Tell the driver not to make a copy of the texture but to use
255 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
256 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
259 /* Use VRAM texturing */
260 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
261 GL_STORAGE_CACHED_APPLE);
263 /* Use AGP texturing */
264 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
265 GL_STORAGE_SHARED_APPLE);
269 /* Call glTexImage2D only once, and use glTexSubImage2D later */
270 if (vgl->buffer[i]) {
271 glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
272 vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
277 vout_opengl_Unlock(vgl->gl);
282 /* XXX See comment vout_display_opengl_Prepare */
283 struct picture_sys_t {
284 vout_display_opengl_t *vgl;
289 static inline GLuint get_texture(picture_t *picture)
291 return *picture->p_sys->texture;
294 static int PictureLock(picture_t *picture)
299 vout_display_opengl_t *vgl = picture->p_sys->vgl;
300 if (!vout_opengl_Lock(vgl->gl)) {
301 glBindTexture(VLCGL_TARGET, get_texture(picture));
302 glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
303 vgl->fmt.i_width, vgl->fmt.i_height,
304 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
306 vout_opengl_Unlock(vgl->gl);
311 static void PictureUnlock(picture_t *picture)
317 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
319 picture_t *picture[VLCGL_TEXTURE_COUNT];
322 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
324 /* TODO memalign would be way better */
325 vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
329 picture_resource_t rsc;
330 memset(&rsc, 0, sizeof(rsc));
332 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
335 rsc.p_sys->vgl = vgl;
336 rsc.p_sys->texture = &vgl->texture[i];
339 rsc.p[0].p_pixels = vgl->buffer[i];
340 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->tex_pixel_size;
341 rsc.p[0].i_lines = vgl->fmt.i_height;
343 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
345 free(vgl->buffer[i]);
346 vgl->buffer[i] = NULL;
350 if (i < VLCGL_TEXTURE_COUNT)
354 picture_pool_configuration_t cfg;
355 memset(&cfg, 0, sizeof(cfg));
356 cfg.picture_count = i;
357 cfg.picture = picture;
359 cfg.lock = PictureLock;
360 cfg.unlock = PictureUnlock;
362 vgl->pool = picture_pool_NewExtended(&cfg);
366 vout_display_opengl_ResetTextures(vgl);
371 for (int j = 0; j < i; j++) {
372 picture_Delete(picture[j]);
373 vgl->buffer[j] = NULL;
378 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
381 /* On Win32/GLX, we do this the usual way:
382 + Fill the buffer with new content,
383 + Reload the texture,
386 On OS X with VRAM or AGP texturing, the order has to be:
387 + Reload the texture,
388 + Fill the buffer with new content,
391 (Thanks to gcc from the Arstechnica forums for the tip)
393 Therefore on OSX, we have to use two buffers and textures and use a
394 lock(/unlock) managed picture pool.
397 if (vout_opengl_Lock(vgl->gl))
401 /* Bind to the texture for drawing */
402 glBindTexture(VLCGL_TARGET, get_texture(picture));
404 /* Update the texture */
405 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
406 vgl->fmt.i_width, vgl->fmt.i_height,
407 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
410 vout_opengl_Unlock(vgl->gl);
414 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
415 const video_format_t *source)
417 if (vout_opengl_Lock(vgl->gl))
420 /* glTexCoord works differently with GL_TEXTURE_2D and
421 GL_TEXTURE_RECTANGLE_EXT */
422 #if VLCGL_TARGET == GL_TEXTURE_2D
423 const float f_normw = vgl->tex_width;
424 const float f_normh = vgl->tex_height;
425 #elif defined (GL_TEXTURE_RECTANGLE_EXT) \
426 && (VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT)
427 const float f_normw = 1.0;
428 const float f_normh = 1.0;
430 # error Unknown texture type!
433 float f_x = (source->i_x_offset + 0 ) / f_normw;
434 float f_y = (source->i_y_offset + 0 ) / f_normh;
435 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
436 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
438 /* Why drawing here and not in Render()? Because this way, the
439 OpenGL providers can call vout_display_opengl_Display to force redraw.i
440 Currently, the OS X provider uses it to get a smooth window resizing */
442 glClear(GL_COLOR_BUFFER_BIT);
444 glEnable(VLCGL_TARGET);
447 static const GLfloat vertexCoord[] = {
454 const GLfloat textureCoord[8] = {
461 glEnableClientState(GL_VERTEX_ARRAY);
462 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
463 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
464 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
466 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
469 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
470 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
471 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
472 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
476 glDisable(VLCGL_TARGET);
478 vout_opengl_Swap(vgl->gl);
480 vout_opengl_Unlock(vgl->gl);