1 /*****************************************************************************
2 * opengl.h: OpenGL vout_display helpers
3 *****************************************************************************
4 * Copyright (C) 2004 the VideoLAN team
5 * Copyright (C) 2009 Laurent Aimar
8 * Authors: Cyril Deguet <asmax@videolan.org>
9 * Gildas Bazin <gbazin@videolan.org>
10 * Eric Petit <titer@m0k.org>
11 * Cedric Cocquebert <cedric.cocquebert@supelec.fr>
12 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_picture_pool.h>
31 #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/gl.h>
37 # include <OpenGLES/ES1/glext.h>
38 #elif USE_OPENGL_ES == 2
39 # include <OpenGLES/ES2/gl.h>
40 # include <OpenGLES/ES2/glext.h>
41 #elif defined(__APPLE__)
43 # include <OpenGL/gl.h>
44 # include <OpenGL/glext.h>
50 # define YCBCR_MESA 0x8757
52 #ifndef UNSIGNED_SHORT_8_8_MESA
53 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
56 #ifndef GL_UNSIGNED_SHORT_5_6_5
57 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
59 #ifndef GL_CLAMP_TO_EDGE
60 # define GL_CLAMP_TO_EDGE 0x812F
65 # define VLCGL_TARGET GL_TEXTURE_2D
67 # define VLCGL_RGB_FORMAT GL_RGB
68 # define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
70 // Use RGB with OpenGLES
71 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
72 # define VLCGL_TYPE VLCGL_RGB_TYPE
74 # define VLCGL_TEXTURE_COUNT (1)
76 #elif defined(MACOS_OPENGL)
78 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
79 This allows sizes which are not powers of 2 */
80 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
82 /* OS X OpenGL supports YUV. Hehe. */
83 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
84 # define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
86 # define VLCGL_TEXTURE_COUNT (2)
89 # define VLCGL_TARGET GL_TEXTURE_2D
92 # define VLCGL_RGB_FORMAT GL_RGBA
93 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
96 # define VLCGL_YUV_FORMAT YCBCR_MESA
97 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
99 /* Use RGB on Win32/GLX */
100 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
101 # define VLCGL_TYPE VLCGL_RGB_TYPE
103 # define VLCGL_TEXTURE_COUNT (1)
106 static inline int GetAlignedSize(int i_size)
108 /* Return the nearest power of 2 */
110 while(i_result < i_size)
125 GLuint texture[VLCGL_TEXTURE_COUNT];
126 uint8_t *buffer[VLCGL_TEXTURE_COUNT];
128 picture_pool_t *pool;
129 } vout_display_opengl_t;
131 static int vout_display_opengl_Init(vout_display_opengl_t *vgl,
137 /* Find the chroma we will use and update fmt */
138 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
139 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
140 fmt->i_chroma = VLC_CODEC_YUYV;
141 vgl->tex_pixel_size = 2;
142 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
143 fmt->i_chroma = VLC_CODEC_UYVY;
144 vgl->tex_pixel_size = 2;
145 #elif VLCGL_FORMAT == GL_RGB
146 # if VLCGL_TYPE == GL_UNSIGNED_BYTE
147 fmt->i_chroma = VLC_CODEC_RGB24;
148 # if defined(WORDS_BIGENDIAN)
149 fmt->i_rmask = 0x00ff0000;
150 fmt->i_gmask = 0x0000ff00;
151 fmt->i_bmask = 0x000000ff;
153 fmt->i_rmask = 0x000000ff;
154 fmt->i_gmask = 0x0000ff00;
155 fmt->i_bmask = 0x00ff0000;
157 vgl->tex_pixel_size = 3;
159 fmt->i_chroma = VLC_CODEC_RGB16;
160 # if defined(WORDS_BIGENDIAN)
161 fmt->i_rmask = 0x001f;
162 fmt->i_gmask = 0x07e0;
163 fmt->i_bmask = 0xf800;
165 fmt->i_rmask = 0xf800;
166 fmt->i_gmask = 0x07e0;
167 fmt->i_bmask = 0x001f;
169 vgl->tex_pixel_size = 2;
172 fmt->i_chroma = VLC_CODEC_RGB32;
173 # if defined(WORDS_BIGENDIAN)
174 fmt->i_rmask = 0xff000000;
175 fmt->i_gmask = 0x00ff0000;
176 fmt->i_bmask = 0x0000ff00;
178 fmt->i_rmask = 0x000000ff;
179 fmt->i_gmask = 0x0000ff00;
180 fmt->i_bmask = 0x00ff0000;
182 vgl->tex_pixel_size = 4;
188 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
190 vgl->buffer[i] = NULL;
194 bool supports_npot = false;
195 #if USE_OPENGL_ES == 2
196 supports_npot = true;
197 #elif defined(MACOS_OPENGL)
198 supports_npot = true;
201 #if defined(__APPLE__) && USE_OPENGL_ES == 1
202 if (!vout_opengl_Lock(vgl->gl)) {
203 const char* extensions = (char*) glGetString(GL_EXTENSIONS);
205 bool npot = strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != 0;
207 supports_npot = true;
209 vout_opengl_Unlock(vgl->gl);
215 vgl->tex_width = fmt->i_width;
216 vgl->tex_height = fmt->i_height;
219 /* A texture must have a size aligned on a power of 2 */
220 vgl->tex_width = GetAlignedSize(fmt->i_width);
221 vgl->tex_height = GetAlignedSize(fmt->i_height);
226 if (!vout_opengl_Lock(vgl->gl)) {
229 glDisable(GL_DEPTH_TEST);
230 glDepthMask(GL_FALSE);
231 glDisable(GL_CULL_FACE);
232 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
233 glClear(GL_COLOR_BUFFER_BIT);
235 vout_opengl_Unlock(vgl->gl);
240 static void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
243 if (!vout_opengl_Lock(vgl->gl)) {
247 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
249 vout_opengl_Unlock(vgl->gl);
252 picture_pool_Delete(vgl->pool);
253 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
254 free(vgl->buffer[i]);
258 static int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
260 if (vout_opengl_Lock(vgl->gl))
263 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
265 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
266 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
267 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
270 /* Set the texture parameters */
271 glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
272 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
275 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
276 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
277 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
278 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
281 /* Tell the driver not to make a copy of the texture but to use
283 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
284 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
287 /* Use VRAM texturing */
288 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
289 GL_STORAGE_CACHED_APPLE);
291 /* Use AGP texturing */
292 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
293 GL_STORAGE_SHARED_APPLE);
297 /* Call glTexImage2D only once, and use glTexSubImage2D later */
298 if (vgl->buffer[i]) {
299 glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
300 vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
305 vout_opengl_Unlock(vgl->gl);
310 /* XXX See comment vout_display_opengl_Prepare */
311 struct picture_sys_t {
312 vout_display_opengl_t *vgl;
317 static inline GLuint get_texture(picture_t *picture)
319 return *picture->p_sys->texture;
322 static int PictureLock(picture_t *picture)
327 vout_display_opengl_t *vgl = picture->p_sys->vgl;
328 if (!vout_opengl_Lock(vgl->gl)) {
329 glBindTexture(VLCGL_TARGET, get_texture(picture));
330 glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
331 vgl->fmt.i_width, vgl->fmt.i_height,
332 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
334 vout_opengl_Unlock(vgl->gl);
338 static void PictureUnlock(picture_t *picture)
344 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
346 picture_t *picture[VLCGL_TEXTURE_COUNT];
349 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
351 /* TODO memalign would be way better */
352 vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
356 picture_resource_t rsc;
357 memset(&rsc, 0, sizeof(rsc));
359 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
362 rsc.p_sys->vgl = vgl;
363 rsc.p_sys->texture = &vgl->texture[i];
366 rsc.p[0].p_pixels = vgl->buffer[i];
367 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->tex_pixel_size;
368 rsc.p[0].i_lines = vgl->fmt.i_height;
370 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
372 free(vgl->buffer[i]);
373 vgl->buffer[i] = NULL;
377 if (i < VLCGL_TEXTURE_COUNT)
381 picture_pool_configuration_t cfg;
382 memset(&cfg, 0, sizeof(cfg));
383 cfg.picture_count = i;
384 cfg.picture = picture;
386 cfg.lock = PictureLock;
387 cfg.unlock = PictureUnlock;
389 vgl->pool = picture_pool_NewExtended(&cfg);
393 vout_display_opengl_ResetTextures(vgl);
398 for (int j = 0; j < i; j++) {
399 picture_Delete(picture[j]);
400 vgl->buffer[j] = NULL;
405 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
408 /* On Win32/GLX, we do this the usual way:
409 + Fill the buffer with new content,
410 + Reload the texture,
413 On OS X with VRAM or AGP texturing, the order has to be:
414 + Reload the texture,
415 + Fill the buffer with new content,
418 (Thanks to gcc from the Arstechnica forums for the tip)
420 Therefore on OSX, we have to use two buffers and textures and use a
421 lock(/unlock) managed picture pool.
424 if (vout_opengl_Lock(vgl->gl))
428 /* Bind to the texture for drawing */
429 glBindTexture(VLCGL_TARGET, get_texture(picture));
431 /* Update the texture */
432 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
433 vgl->fmt.i_width, vgl->fmt.i_height,
434 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
437 vout_opengl_Unlock(vgl->gl);
441 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
442 const video_format_t *source)
444 if (vout_opengl_Lock(vgl->gl))
447 /* glTexCoord works differently with GL_TEXTURE_2D and
448 GL_TEXTURE_RECTANGLE_EXT */
449 #if VLCGL_TARGET == GL_TEXTURE_2D
450 const float f_normw = vgl->tex_width;
451 const float f_normh = vgl->tex_height;
453 assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
454 const float f_normw = 1.0;
455 const float f_normh = 1.0;
458 float f_x = (source->i_x_offset + 0 ) / f_normw;
459 float f_y = (source->i_y_offset + 0 ) / f_normh;
460 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
461 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
463 /* Why drawing here and not in Render()? Because this way, the
464 OpenGL providers can call vout_display_opengl_Display to force redraw.i
465 Currently, the OS X provider uses it to get a smooth window resizing */
467 glClear(GL_COLOR_BUFFER_BIT);
469 glEnable(VLCGL_TARGET);
472 static const GLfloat vertexCoord[] = {
479 const GLfloat textureCoord[8] = {
486 glEnableClientState(GL_VERTEX_ARRAY);
487 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
488 glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
489 glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
491 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
494 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
495 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
496 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
497 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
501 glDisable(VLCGL_TARGET);
503 vout_opengl_Swap(vgl->gl);
505 vout_opengl_Unlock(vgl->gl);