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;
270 static inline GLuint get_texture(picture_t *picture)
272 return *picture->p_sys->texture;
275 static int PictureLock(picture_t *picture)
280 vout_display_opengl_t *vgl = picture->p_sys->vgl;
281 if (!vout_opengl_Lock(vgl->gl)) {
283 glBindTexture(VLCGL_TARGET, get_texture(picture));
284 glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
285 vgl->fmt.i_width, vgl->fmt.i_height,
286 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
288 vout_opengl_Unlock(vgl->gl);
292 static void PictureUnlock(picture_t *picture)
298 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
300 picture_t *picture[VLCGL_TEXTURE_COUNT];
303 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
305 /* TODO memalign would be way better */
306 vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
310 picture_resource_t rsc;
311 memset(&rsc, 0, sizeof(rsc));
313 rsc.p_sys = malloc(sizeof(*rsc.p_sys));
316 rsc.p_sys->vgl = vgl;
317 rsc.p_sys->texture = &vgl->texture[i];
320 rsc.p[0].p_pixels = vgl->buffer[i];
321 rsc.p[0].i_pitch = vgl->fmt.i_width * vgl->tex_pixel_size;
322 rsc.p[0].i_lines = vgl->fmt.i_height;
324 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
326 free(vgl->buffer[i]);
327 vgl->buffer[i] = NULL;
331 if (i < VLCGL_TEXTURE_COUNT)
335 picture_pool_configuration_t cfg;
336 memset(&cfg, 0, sizeof(cfg));
337 cfg.picture_count = i;
338 cfg.picture = picture;
340 cfg.lock = PictureLock;
341 cfg.unlock = PictureUnlock;
343 vgl->pool = picture_pool_NewExtended(&cfg);
347 vout_display_opengl_ResetTextures(vgl);
352 for (int j = 0; j < i; j++) {
353 picture_Delete(picture[j]);
354 vgl->buffer[j] = NULL;
359 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
362 /* On Win32/GLX, we do this the usual way:
363 + Fill the buffer with new content,
364 + Reload the texture,
367 On OS X with VRAM or AGP texturing, the order has to be:
368 + Reload the texture,
369 + Fill the buffer with new content,
372 (Thanks to gcc from the Arstechnica forums for the tip)
374 Therefore on OSX, we have to use two buffers and textures and use a
375 lock(/unlock) managed picture pool.
378 if (vout_opengl_Lock(vgl->gl))
382 /* Bind to the texture for drawing */
383 glBindTexture(VLCGL_TARGET, get_texture(picture));
385 /* Update the texture */
386 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
387 vgl->fmt.i_width, vgl->fmt.i_height,
388 VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
391 vout_opengl_Unlock(vgl->gl);
395 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
396 const video_format_t *source)
398 if (vout_opengl_Lock(vgl->gl))
401 /* glTexCoord works differently with GL_TEXTURE_2D and
402 GL_TEXTURE_RECTANGLE_EXT */
403 #if VLCGL_TARGET == GL_TEXTURE_2D
404 const float f_normw = vgl->tex_width;
405 const float f_normh = vgl->tex_height;
407 assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
408 const float f_normw = 1.0;
409 const float f_normh = 1.0;
412 float f_x = (source->i_x_offset + 0 ) / f_normw;
413 float f_y = (source->i_y_offset + 0 ) / f_normh;
414 float f_width = (source->i_x_offset + source->i_visible_width ) / f_normw;
415 float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
417 /* Why drawing here and not in Render()? Because this way, the
418 OpenGL providers can call vout_display_opengl_Display to force redraw.i
419 Currently, the OS X provider uses it to get a smooth window resizing */
421 glClear(GL_COLOR_BUFFER_BIT);
423 glEnable(VLCGL_TARGET);
426 glTexCoord2f(f_x, f_y); glVertex2f(-1.0, 1.0);
427 glTexCoord2f(f_width, f_y); glVertex2f( 1.0, 1.0);
428 glTexCoord2f(f_width, f_height); glVertex2f( 1.0, -1.0);
429 glTexCoord2f(f_x, f_height); glVertex2f(-1.0, -1.0);
432 glDisable(VLCGL_TARGET);
434 vout_opengl_Swap(vgl->gl);
436 vout_opengl_Unlock(vgl->gl);