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>
34 # include <OpenGL/gl.h>
35 # include <OpenGL/glext.h>
41 # define YCBCR_MESA 0x8757
43 #ifndef UNSIGNED_SHORT_8_8_MESA
44 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
47 #ifndef GL_UNSIGNED_SHORT_5_6_5
48 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
50 #ifndef GL_CLAMP_TO_EDGE
51 # define GL_CLAMP_TO_EDGE 0x812F
55 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
56 This allows sizes which are not powers of 2 */
57 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
59 /* OS X OpenGL supports YUV. Hehe. */
60 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
61 # define VLCGL_TYPE GL_UNSIGNED_SHORT_8_8_APPLE
63 # define VLCGL_TEXTURE_COUNT (2)
66 # define VLCGL_TARGET GL_TEXTURE_2D
69 # define VLCGL_RGB_FORMAT GL_RGBA
70 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
73 # define VLCGL_YUV_FORMAT YCBCR_MESA
74 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
76 /* Use RGB on Win32/GLX */
77 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
78 # define VLCGL_TYPE VLCGL_RGB_TYPE
80 # define VLCGL_TEXTURE_COUNT (1)
83 static inline int GetAlignedSize(int i_size)
85 /* Return the nearest power of 2 */
87 while(i_result < i_size)
102 GLuint texture[VLCGL_TEXTURE_COUNT];
103 uint8_t *buffer[VLCGL_TEXTURE_COUNT];
105 picture_pool_t *pool;
106 } vout_display_opengl_t;
108 static int vout_display_opengl_Init(vout_display_opengl_t *vgl,
114 /* Find the chroma we will use and update fmt */
115 /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
116 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
117 fmt->i_chroma = VLC_CODEC_YUYV;
118 vgl->tex_pixel_size = 2;
119 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
120 fmt->i_chroma = VLC_CODEC_UYVY;
121 vgl->tex_pixel_size = 2;
122 #elif VLCGL_FORMAT == GL_RGB
123 # if VLCGL_TYPE == GL_UNSIGNED_BYTE
124 fmt->i_chroma = VLC_CODEC_RGB24;
125 # if defined(WORDS_BIGENDIAN)
126 fmt->i_rmask = 0x00ff0000;
127 fmt->i_gmask = 0x0000ff00;
128 fmt->i_bmask = 0x000000ff;
130 fmt->i_rmask = 0x000000ff;
131 fmt->i_gmask = 0x0000ff00;
132 fmt->i_bmask = 0x00ff0000;
134 vgl->tex_pixel_size = 3;
136 fmt->i_chroma = VLC_CODEC_RGB16;
137 # if defined(WORDS_BIGENDIAN)
138 fmt->i_rmask = 0x001f;
139 fmt->i_gmask = 0x07e0;
140 fmt->i_bmask = 0xf800;
142 fmt->i_rmask = 0xf800;
143 fmt->i_gmask = 0x07e0;
144 fmt->i_bmask = 0x001f;
146 vgl->tex_pixel_size = 2;
149 fmt->i_chroma = VLC_CODEC_RGB32;
150 # if defined(WORDS_BIGENDIAN)
151 fmt->i_rmask = 0xff000000;
152 fmt->i_gmask = 0x00ff0000;
153 fmt->i_bmask = 0x0000ff00;
155 fmt->i_rmask = 0x000000ff;
156 fmt->i_gmask = 0x0000ff00;
157 fmt->i_bmask = 0x00ff0000;
159 vgl->tex_pixel_size = 4;
166 vgl->tex_width = fmt->i_width;
167 vgl->tex_height = fmt->i_height;
169 /* A texture must have a size aligned on a power of 2 */
170 vgl->tex_width = GetAlignedSize(fmt->i_width);
171 vgl->tex_height = GetAlignedSize(fmt->i_height);
175 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
177 vgl->buffer[i] = NULL;
182 if (!vout_opengl_Lock(vgl->gl)) {
185 glDisable(GL_DEPTH_TEST);
186 glDepthMask(GL_FALSE);
187 glDisable(GL_CULL_FACE);
188 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
189 glClear(GL_COLOR_BUFFER_BIT);
191 vout_opengl_Unlock(vgl->gl);
195 static void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
198 if (!vout_opengl_Lock(vgl->gl)) {
202 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
204 vout_opengl_Unlock(vgl->gl);
207 picture_pool_Delete(vgl->pool);
208 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
209 free(vgl->buffer[i]);
213 static int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
215 if (vout_opengl_Lock(vgl->gl))
218 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
220 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
221 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
222 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
224 /* Set the texture parameters */
225 glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
227 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
228 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
230 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
231 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
233 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
236 /* Tell the driver not to make a copy of the texture but to use
238 glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
239 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
242 /* Use VRAM texturing */
243 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
244 GL_STORAGE_CACHED_APPLE);
246 /* Use AGP texturing */
247 glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
248 GL_STORAGE_SHARED_APPLE);
252 /* Call glTexImage2D only once, and use glTexSubImage2D later */
254 glTexImage2D(VLCGL_TARGET, 0, 3, vgl->tex_width, vgl->tex_height,
255 0, VLCGL_FORMAT, VLCGL_TYPE, vgl->buffer[i]);
258 vout_opengl_Unlock(vgl->gl);
263 /* XXX See comment vout_display_opengl_Prepare */
264 struct picture_sys_t {
265 vout_display_opengl_t *vgl;
268 static int PictureLock(picture_t *picture)
273 vout_display_opengl_t *vgl = picture->p_sys->vgl;
274 if (!vout_opengl_Lock(vgl->gl)) {
276 glBindTexture(VLCGL_TARGET, picture->p_sys->texture);
277 glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
278 vgl->fmt.i_width, vgl->fmt.i_height,
279 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
281 vout_opengl_Unlock(vgl->gl);
285 static void PictureUnlock(picture_t *picture)
291 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
293 picture_t *picture[VLCGL_TEXTURE_COUNT];
296 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
298 /* TODO memalign would be way better */
299 vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
303 picture_resource_t rsc;
304 memset(&rsc, 0, sizeof(rsc));
306 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
308 rsc.p_sys->vgl = vgl;
310 rsc.p[0].p_pixels = vgl->buffer[i];
311 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->tex_pixel_size;
312 rsc.p[0].i_lines = vgl->fmt.i_height;
314 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
316 free(vgl->buffer[i]);
317 vgl->buffer[i] = NULL;
321 if (i < VLCGL_TEXTURE_COUNT)
325 picture_pool_configuration_t cfg;
326 memset(&cfg, 0, sizeof(cfg));
327 cfg.picture_count = i;
328 cfg.picture = picture;
330 cfg.lock = PictureLock;
331 cfg.unlock = PictureUnlock;
333 vgl->pool = picture_pool_NewExtended(&cfg);
337 vout_display_opengl_ResetTextures(vgl);
342 for (int j = 0; j < i; j++) {
343 picture_Delete(picture[j]);
344 vgl->buffer[j] = NULL;
349 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
352 /* On Win32/GLX, we do this the usual way:
353 + Fill the buffer with new content,
354 + Reload the texture,
357 On OS X with VRAM or AGP texturing, the order has to be:
358 + Reload the texture,
359 + Fill the buffer with new content,
362 (Thanks to gcc from the Arstechnica forums for the tip)
364 Therefore on OSX, we have to use two buffers and textures and use a
365 lock(/unlock) managed picture pool.
368 if (vout_opengl_Lock(vgl->gl))
372 /* Bind to the texture for drawing */
373 glBindTexture(VLCGL_TARGET, picture->p_sys->texture);
375 /* Update the texture */
376 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
377 vgl->fmt.i_width, vgl->fmt.i_height,
378 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
381 vout_opengl_Unlock(vgl->gl);
385 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
386 const video_format_t *source)
388 if (vout_opengl_Lock(vgl->gl))
391 /* glTexCoord works differently with GL_TEXTURE_2D and
392 GL_TEXTURE_RECTANGLE_EXT */
393 #if VLCGL_TARGET == GL_TEXTURE_2D
394 const float f_normw = vgl->tex_width;
395 const float f_normh = vgl->tex_height;
397 assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
398 const float f_normw = 1.0;
399 const float f_normh = 1.0;
402 float f_x = (source->i_x_offset + 0 ) / f_normw;
403 float f_y = (source->i_y_offset + 0 ) / f_normh;
404 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
405 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
407 /* Why drawing here and not in Render()? Because this way, the
408 OpenGL providers can call vout_display_opengl_Display to force redraw.i
409 Currently, the OS X provider uses it to get a smooth window resizing */
411 glClear(GL_COLOR_BUFFER_BIT);
413 glEnable(VLCGL_TARGET);
416 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
417 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
418 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
419 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
422 glDisable(VLCGL_TARGET);
424 vout_opengl_Swap(vgl->gl);
426 vout_opengl_Unlock(vgl->gl);