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 # define YCBCR_MESA 0x8757
57 #ifndef UNSIGNED_SHORT_8_8_MESA
58 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
61 #ifndef GL_UNSIGNED_SHORT_5_6_5
62 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
64 #ifndef GL_CLAMP_TO_EDGE
65 # define GL_CLAMP_TO_EDGE 0x812F
70 # define VLCGL_TARGET GL_TEXTURE_2D
72 # define VLCGL_RGB_FORMAT GL_RGB
73 # define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
75 // Use RGB with OpenGLES
76 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
77 # define VLCGL_TYPE VLCGL_RGB_TYPE
79 #elif defined(MACOS_OPENGL)
81 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
82 This allows sizes which are not powers of 2 */
83 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
85 /* OS X OpenGL supports YUV. Hehe. */
86 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
87 # define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
91 # define VLCGL_TARGET GL_TEXTURE_2D
94 # define VLCGL_RGB_FORMAT GL_RGBA
95 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
98 # define VLCGL_YUV_FORMAT YCBCR_MESA
99 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
101 /* Use RGB on Win32/GLX */
102 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
103 # define VLCGL_TYPE VLCGL_RGB_TYPE
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 int vout_display_opengl_Init(vout_display_opengl_t
*vgl
,
119 /* Find the chroma we will use and update fmt */
120 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
121 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
122 fmt
->i_chroma
= VLC_CODEC_YUYV
;
123 vgl
->tex_pixel_size
= 2;
124 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
125 fmt
->i_chroma
= VLC_CODEC_UYVY
;
126 vgl
->tex_pixel_size
= 2;
127 #elif VLCGL_FORMAT == GL_RGB
128 # if VLCGL_TYPE == GL_UNSIGNED_BYTE
129 fmt
->i_chroma
= VLC_CODEC_RGB24
;
130 # if defined(WORDS_BIGENDIAN)
131 fmt
->i_rmask
= 0x00ff0000;
132 fmt
->i_gmask
= 0x0000ff00;
133 fmt
->i_bmask
= 0x000000ff;
135 fmt
->i_rmask
= 0x000000ff;
136 fmt
->i_gmask
= 0x0000ff00;
137 fmt
->i_bmask
= 0x00ff0000;
139 vgl
->tex_pixel_size
= 3;
141 fmt
->i_chroma
= VLC_CODEC_RGB16
;
142 # if defined(WORDS_BIGENDIAN)
143 fmt
->i_rmask
= 0x001f;
144 fmt
->i_gmask
= 0x07e0;
145 fmt
->i_bmask
= 0xf800;
147 fmt
->i_rmask
= 0xf800;
148 fmt
->i_gmask
= 0x07e0;
149 fmt
->i_bmask
= 0x001f;
151 vgl
->tex_pixel_size
= 2;
154 fmt
->i_chroma
= VLC_CODEC_RGB32
;
155 # if defined(WORDS_BIGENDIAN)
156 fmt
->i_rmask
= 0xff000000;
157 fmt
->i_gmask
= 0x00ff0000;
158 fmt
->i_bmask
= 0x0000ff00;
160 fmt
->i_rmask
= 0x000000ff;
161 fmt
->i_gmask
= 0x0000ff00;
162 fmt
->i_bmask
= 0x00ff0000;
164 vgl
->tex_pixel_size
= 4;
170 for (int i
= 0; i
< VLCGL_TEXTURE_COUNT
; i
++) {
172 vgl
->buffer
[i
] = NULL
;
176 bool supports_npot
= false;
177 #if USE_OPENGL_ES == 2
178 supports_npot
= true;
179 #elif defined(MACOS_OPENGL)
180 supports_npot
= true;
183 #if defined(__APPLE__) && USE_OPENGL_ES == 1
184 if (!vlc_gl_Lock(vgl
->gl
)) {
185 const char* extensions
= (char*) glGetString(GL_EXTENSIONS
);
187 bool npot
= strstr(extensions
, "GL_APPLE_texture_2D_limited_npot") != 0;
189 supports_npot
= true;
191 vlc_gl_Unlock(vgl
->gl
);
197 vgl
->tex_width
= fmt
->i_width
;
198 vgl
->tex_height
= fmt
->i_height
;
201 /* A texture must have a size aligned on a power of 2 */
202 vgl
->tex_width
= GetAlignedSize(fmt
->i_width
);
203 vgl
->tex_height
= GetAlignedSize(fmt
->i_height
);
208 if (!vlc_gl_Lock(vgl
->gl
)) {
211 glDisable(GL_DEPTH_TEST
);
212 glDepthMask(GL_FALSE
);
213 glDisable(GL_CULL_FACE
);
214 glClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
215 glClear(GL_COLOR_BUFFER_BIT
);
217 vlc_gl_Unlock(vgl
->gl
);
222 void vout_display_opengl_Clean(vout_display_opengl_t
*vgl
)
225 if (!vlc_gl_Lock(vgl
->gl
)) {
229 glDeleteTextures(VLCGL_TEXTURE_COUNT
, vgl
->texture
);
231 vlc_gl_Unlock(vgl
->gl
);
234 picture_pool_Delete(vgl
->pool
);
235 for (int i
= 0; i
< VLCGL_TEXTURE_COUNT
; i
++)
236 free(vgl
->buffer
[i
]);
240 int vout_display_opengl_ResetTextures(vout_display_opengl_t
*vgl
)
242 if (vlc_gl_Lock(vgl
->gl
))
245 glDeleteTextures(VLCGL_TEXTURE_COUNT
, vgl
->texture
);
247 glGenTextures(VLCGL_TEXTURE_COUNT
, vgl
->texture
);
248 for (int i
= 0; i
< VLCGL_TEXTURE_COUNT
; i
++) {
249 glBindTexture(VLCGL_TARGET
, vgl
->texture
[i
]);
252 /* Set the texture parameters */
253 glTexParameterf(VLCGL_TARGET
, GL_TEXTURE_PRIORITY
, 1.0);
254 glTexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
257 glTexParameteri(VLCGL_TARGET
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
258 glTexParameteri(VLCGL_TARGET
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
259 glTexParameteri(VLCGL_TARGET
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
260 glTexParameteri(VLCGL_TARGET
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
263 /* Tell the driver not to make a copy of the texture but to use
265 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE
);
266 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_TRUE
);
269 /* Use VRAM texturing */
270 glTexParameteri(VLCGL_TARGET
, GL_TEXTURE_STORAGE_HINT_APPLE
,
271 GL_STORAGE_CACHED_APPLE
);
273 /* Use AGP texturing */
274 glTexParameteri(VLCGL_TARGET
, GL_TEXTURE_STORAGE_HINT_APPLE
,
275 GL_STORAGE_SHARED_APPLE
);
279 /* Call glTexImage2D only once, and use glTexSubImage2D later */
280 if (vgl
->buffer
[i
]) {
281 glTexImage2D(VLCGL_TARGET
, 0, VLCGL_FORMAT
, vgl
->tex_width
,
282 vgl
->tex_height
, 0, VLCGL_FORMAT
, VLCGL_TYPE
,
287 vlc_gl_Unlock(vgl
->gl
);
292 /* XXX See comment vout_display_opengl_Prepare */
293 struct picture_sys_t
{
294 vout_display_opengl_t
*vgl
;
299 static inline GLuint
get_texture(picture_t
*picture
)
301 return *picture
->p_sys
->texture
;
304 static int PictureLock(picture_t
*picture
)
309 vout_display_opengl_t
*vgl
= picture
->p_sys
->vgl
;
310 if (!vlc_gl_Lock(vgl
->gl
)) {
311 glBindTexture(VLCGL_TARGET
, get_texture(picture
));
312 glTexSubImage2D(VLCGL_TARGET
, 0, 0, 0,
313 vgl
->fmt
.i_width
, vgl
->fmt
.i_height
,
314 VLCGL_FORMAT
, VLCGL_TYPE
, picture
->p
[0].p_pixels
);
316 vlc_gl_Unlock(vgl
->gl
);
321 static void PictureUnlock(picture_t
*picture
)
327 picture_pool_t
*vout_display_opengl_GetPool(vout_display_opengl_t
*vgl
)
329 picture_t
*picture
[VLCGL_TEXTURE_COUNT
];
332 for (i
= 0; i
< VLCGL_TEXTURE_COUNT
; i
++) {
334 /* TODO memalign would be way better */
335 vgl
->buffer
[i
] = malloc(vgl
->tex_width
* vgl
->tex_height
* vgl
->tex_pixel_size
);
339 picture_resource_t rsc
;
340 memset(&rsc
, 0, sizeof(rsc
));
342 rsc
.p_sys
= malloc(sizeof(*rsc
.p_sys
));
345 rsc
.p_sys
->vgl
= vgl
;
346 rsc
.p_sys
->texture
= &vgl
->texture
[i
];
349 rsc
.p
[0].p_pixels
= vgl
->buffer
[i
];
350 rsc
.p
[0].i_pitch
= vgl
->fmt
.i_width
* vgl
->tex_pixel_size
;
351 rsc
.p
[0].i_lines
= vgl
->fmt
.i_height
;
353 picture
[i
] = picture_NewFromResource(&vgl
->fmt
, &rsc
);
355 free(vgl
->buffer
[i
]);
356 vgl
->buffer
[i
] = NULL
;
360 if (i
< VLCGL_TEXTURE_COUNT
)
364 picture_pool_configuration_t cfg
;
365 memset(&cfg
, 0, sizeof(cfg
));
366 cfg
.picture_count
= i
;
367 cfg
.picture
= picture
;
369 cfg
.lock
= PictureLock
;
370 cfg
.unlock
= PictureUnlock
;
372 vgl
->pool
= picture_pool_NewExtended(&cfg
);
376 vout_display_opengl_ResetTextures(vgl
);
381 for (int j
= 0; j
< i
; j
++) {
382 picture_Delete(picture
[j
]);
383 vgl
->buffer
[j
] = NULL
;
388 int vout_display_opengl_Prepare(vout_display_opengl_t
*vgl
,
391 /* On Win32/GLX, we do this the usual way:
392 + Fill the buffer with new content,
393 + Reload the texture,
396 On OS X with VRAM or AGP texturing, the order has to be:
397 + Reload the texture,
398 + Fill the buffer with new content,
401 (Thanks to gcc from the Arstechnica forums for the tip)
403 Therefore on OSX, we have to use two buffers and textures and use a
404 lock(/unlock) managed picture pool.
407 if (vlc_gl_Lock(vgl
->gl
))
411 /* Bind to the texture for drawing */
412 glBindTexture(VLCGL_TARGET
, get_texture(picture
));
414 /* Update the texture */
415 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0,
416 vgl
->fmt
.i_width
, vgl
->fmt
.i_height
,
417 VLCGL_FORMAT
, VLCGL_TYPE
, picture
->p
[0].p_pixels
);
420 vlc_gl_Unlock(vgl
->gl
);
424 int vout_display_opengl_Display(vout_display_opengl_t
*vgl
,
425 const video_format_t
*source
)
427 if (vlc_gl_Lock(vgl
->gl
))
430 /* glTexCoord works differently with GL_TEXTURE_2D and
431 GL_TEXTURE_RECTANGLE_EXT */
432 #if VLCGL_TARGET == GL_TEXTURE_2D
433 const float f_normw
= vgl
->tex_width
;
434 const float f_normh
= vgl
->tex_height
;
435 #elif defined (GL_TEXTURE_RECTANGLE_EXT) \
436 && (VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT)
437 const float f_normw
= 1.0;
438 const float f_normh
= 1.0;
440 # error Unknown texture type!
443 float f_x
= (source
->i_x_offset
+ 0 ) / f_normw
;
444 float f_y
= (source
->i_y_offset
+ 0 ) / f_normh
;
445 float f_width
= (source
->i_x_offset
+ source
->i_visible_width
) / f_normw
;
446 float f_height
= (source
->i_y_offset
+ source
->i_visible_height
) / f_normh
;
448 /* Why drawing here and not in Render()? Because this way, the
449 OpenGL providers can call vout_display_opengl_Display to force redraw.i
450 Currently, the OS X provider uses it to get a smooth window resizing */
452 glClear(GL_COLOR_BUFFER_BIT
);
454 glEnable(VLCGL_TARGET
);
457 static const GLfloat vertexCoord
[] = {
464 const GLfloat textureCoord
[8] = {
471 glEnableClientState(GL_VERTEX_ARRAY
);
472 glEnableClientState(GL_TEXTURE_COORD_ARRAY
);
473 glVertexPointer(2, GL_FLOAT
, 0, vertexCoord
);
474 glTexCoordPointer(2, GL_FLOAT
, 0, textureCoord
);
476 glDrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
479 glTexCoord2f(f_x
, f_y
); glVertex2f(-1.0, 1.0);
480 glTexCoord2f(f_width
, f_y
); glVertex2f( 1.0, 1.0);
481 glTexCoord2f(f_width
, f_height
); glVertex2f( 1.0, -1.0);
482 glTexCoord2f(f_x
, f_height
); glVertex2f(-1.0, -1.0);
486 glDisable(VLCGL_TARGET
);
488 vlc_gl_Swap(vgl
->gl
);
490 vlc_gl_Unlock(vgl
->gl
);