opengl.h

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * opengl.h: OpenGL vout_display helpers
00003  *****************************************************************************
00004  * Copyright (C) 2004 the VideoLAN team
00005  * Copyright (C) 2009 Laurent Aimar
00006  * $Id: c54258fc863ea51c38e2bfc134a189d9f6f4636b $
00007  *
00008  * Authors: Cyril Deguet <asmax@videolan.org>
00009  *          Gildas Bazin <gbazin@videolan.org>
00010  *          Eric Petit <titer@m0k.org>
00011  *          Cedric Cocquebert <cedric.cocquebert@supelec.fr>
00012  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
00013  *
00014  * This program is free software; you can redistribute it and/or modify
00015  * it under the terms of the GNU General Public License as published by
00016  * the Free Software Foundation; either version 2 of the License, or
00017  * (at your option) any later version.
00018  *
00019  * This program is distributed in the hope that it will be useful,
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022  * GNU General Public License for more details.
00023  *
00024  * You should have received a copy of the GNU General Public License
00025  * along with this program; if not, write to the Free Software
00026  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
00027  *****************************************************************************/
00028 
00029 #include <vlc_common.h>
00030 #include <vlc_picture_pool.h>
00031 #include <vlc_vout_opengl.h>
00032 
00033 #ifdef __APPLE__
00034 # include <OpenGL/gl.h>
00035 # include <OpenGL/glext.h>
00036 #else
00037 # include <GL/gl.h>
00038 #endif
00039 
00040 #ifndef YCBCR_MESA
00041 # define YCBCR_MESA 0x8757
00042 #endif
00043 #ifndef UNSIGNED_SHORT_8_8_MESA
00044 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
00045 #endif
00046 /* RV16 */
00047 #ifndef GL_UNSIGNED_SHORT_5_6_5
00048 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
00049 #endif
00050 #ifndef GL_CLAMP_TO_EDGE
00051 # define GL_CLAMP_TO_EDGE 0x812F
00052 #endif
00053 
00054 #ifdef __APPLE__
00055 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
00056    This allows sizes which are not powers of 2 */
00057 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
00058 
00059 /* OS X OpenGL supports YUV. Hehe. */
00060 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
00061 # define VLCGL_TYPE   GL_UNSIGNED_SHORT_8_8_APPLE
00062 
00063 # define VLCGL_TEXTURE_COUNT (2)
00064 #else
00065 
00066 # define VLCGL_TARGET GL_TEXTURE_2D
00067 
00068 /* RV32 */
00069 # define VLCGL_RGB_FORMAT GL_RGBA
00070 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
00071 
00072 /* YUY2 */
00073 # define VLCGL_YUV_FORMAT YCBCR_MESA
00074 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
00075 
00076 /* Use RGB on Win32/GLX */
00077 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
00078 # define VLCGL_TYPE   VLCGL_RGB_TYPE
00079 
00080 # define VLCGL_TEXTURE_COUNT (1)
00081 #endif
00082 
00083 static inline int GetAlignedSize(int i_size)
00084 {
00085     /* Return the nearest power of 2 */
00086     int i_result = 1;
00087     while(i_result < i_size)
00088         i_result *= 2;
00089 
00090     return i_result;
00091 }
00092 
00093 typedef struct {
00094     vout_opengl_t  *gl;
00095 
00096     video_format_t fmt;
00097 
00098     int        tex_pixel_size;
00099     int        tex_width;
00100     int        tex_height;
00101 
00102     GLuint     texture[VLCGL_TEXTURE_COUNT];
00103     uint8_t    *buffer[VLCGL_TEXTURE_COUNT];
00104 
00105     picture_pool_t *pool;
00106 } vout_display_opengl_t;
00107 
00108 static int vout_display_opengl_Init(vout_display_opengl_t *vgl,
00109                                     video_format_t *fmt,
00110                                     vout_opengl_t *gl)
00111 {
00112     vgl->gl = gl;
00113 
00114     /* Find the chroma we will use and update fmt */
00115     /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
00116 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
00117     fmt->i_chroma = VLC_CODEC_YUYV;
00118     vgl->tex_pixel_size = 2;
00119 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
00120     fmt->i_chroma = VLC_CODEC_UYVY;
00121     vgl->tex_pixel_size = 2;
00122 #elif VLCGL_FORMAT == GL_RGB
00123 #   if VLCGL_TYPE == GL_UNSIGNED_BYTE
00124     fmt->i_chroma = VLC_CODEC_RGB24;
00125 #       if defined(WORDS_BIGENDIAN)
00126     fmt->i_rmask = 0x00ff0000;
00127     fmt->i_gmask = 0x0000ff00;
00128     fmt->i_bmask = 0x000000ff;
00129 #       else
00130     fmt->i_rmask = 0x000000ff;
00131     fmt->i_gmask = 0x0000ff00;
00132     fmt->i_bmask = 0x00ff0000;
00133 #       endif
00134     vgl->tex_pixel_size = 3;
00135 #   else
00136     fmt->i_chroma = VLC_CODEC_RGB16;
00137 #       if defined(WORDS_BIGENDIAN)
00138     fmt->i_rmask = 0x001f;
00139     fmt->i_gmask = 0x07e0;
00140     fmt->i_bmask = 0xf800;
00141 #       else
00142     fmt->i_rmask = 0xf800;
00143     fmt->i_gmask = 0x07e0;
00144     fmt->i_bmask = 0x001f;
00145 #       endif
00146     vgl->tex_pixel_size = 2;
00147 #   endif
00148 #else
00149     fmt->i_chroma = VLC_CODEC_RGB32;
00150 #       if defined(WORDS_BIGENDIAN)
00151     fmt->i_rmask = 0xff000000;
00152     fmt->i_gmask = 0x00ff0000;
00153     fmt->i_bmask = 0x0000ff00;
00154 #       else
00155     fmt->i_rmask = 0x000000ff;
00156     fmt->i_gmask = 0x0000ff00;
00157     fmt->i_bmask = 0x00ff0000;
00158 #       endif
00159     vgl->tex_pixel_size = 4;
00160 #endif
00161 
00162     vgl->fmt = *fmt;
00163 
00164     /* Texture size */
00165 #ifdef __APPLE__
00166     vgl->tex_width  = fmt->i_width;
00167     vgl->tex_height = fmt->i_height;
00168 #else
00169     /* A texture must have a size aligned on a power of 2 */
00170     vgl->tex_width  = GetAlignedSize(fmt->i_width);
00171     vgl->tex_height = GetAlignedSize(fmt->i_height);
00172 #endif
00173 
00174     /* */
00175     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
00176         vgl->texture[i] = 0;
00177         vgl->buffer[i]  = NULL;
00178     }
00179     vgl->pool = NULL;
00180 
00181     /* */
00182     if (!vout_opengl_Lock(vgl->gl)) {
00183 
00184         glDisable(GL_BLEND);
00185         glDisable(GL_DEPTH_TEST);
00186         glDepthMask(GL_FALSE);
00187         glDisable(GL_CULL_FACE);
00188         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
00189         glClear(GL_COLOR_BUFFER_BIT);
00190 
00191         vout_opengl_Unlock(vgl->gl);
00192     }
00193     return VLC_SUCCESS;
00194 }
00195 static void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
00196 {
00197     /* */
00198     if (!vout_opengl_Lock(vgl->gl)) {
00199 
00200         glFinish();
00201         glFlush();
00202         glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
00203 
00204         vout_opengl_Unlock(vgl->gl);
00205     }
00206     if (vgl->pool) {
00207         picture_pool_Delete(vgl->pool);
00208         for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
00209             free(vgl->buffer[i]);
00210     }
00211 }
00212 
00213 static int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
00214 {
00215     if (vout_opengl_Lock(vgl->gl))
00216         return VLC_EGENERIC;
00217 
00218     glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
00219 
00220     glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
00221     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
00222         glBindTexture(VLCGL_TARGET, vgl->texture[i]);
00223 
00224         /* Set the texture parameters */
00225         glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
00226 
00227         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00228         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00229 
00230         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00231         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00232 
00233         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
00234 
00235 #ifdef __APPLE__
00236         /* Tell the driver not to make a copy of the texture but to use
00237            our buffer */
00238         glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
00239         glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
00240 
00241 #if 0
00242         /* Use VRAM texturing */
00243         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
00244                          GL_STORAGE_CACHED_APPLE);
00245 #else
00246         /* Use AGP texturing */
00247         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
00248                          GL_STORAGE_SHARED_APPLE);
00249 #endif
00250 #endif
00251 
00252         /* Call glTexImage2D only once, and use glTexSubImage2D later */
00253         if (vgl->buffer[i])
00254             glTexImage2D(VLCGL_TARGET, 0, 3, vgl->tex_width, vgl->tex_height,
00255                          0, VLCGL_FORMAT, VLCGL_TYPE, vgl->buffer[i]);
00256     }
00257 
00258     vout_opengl_Unlock(vgl->gl);
00259     return VLC_SUCCESS;
00260 }
00261 
00262 #ifdef __APPLE__
00263 /* XXX See comment vout_display_opengl_Prepare */
00264 struct picture_sys_t {
00265     vout_display_opengl_t *vgl;
00266     GLuint *texture;
00267 };
00268 
00269 /* Small helper */
00270 static inline GLuint get_texture(picture_t *picture)
00271 {
00272     return *picture->p_sys->texture;
00273 }
00274 
00275 static int PictureLock(picture_t *picture)
00276 {
00277     if (!picture->p_sys)
00278         return VLC_SUCCESS;
00279 
00280     vout_display_opengl_t *vgl = picture->p_sys->vgl;
00281     if (!vout_opengl_Lock(vgl->gl)) {
00282 
00283         glBindTexture(VLCGL_TARGET, get_texture(picture));
00284         glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
00285                         vgl->fmt.i_width, vgl->fmt.i_height,
00286                         VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
00287 
00288         vout_opengl_Unlock(vgl->gl);
00289     }
00290     return VLC_SUCCESS;
00291 }
00292 static void PictureUnlock(picture_t *picture)
00293 {
00294     VLC_UNUSED(picture);
00295 }
00296 #endif
00297 
00298 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
00299 {
00300     picture_t *picture[VLCGL_TEXTURE_COUNT];
00301 
00302     int i;
00303     for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
00304 
00305         /* TODO memalign would be way better */
00306         vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
00307         if (!vgl->buffer[i])
00308             break;
00309 
00310         picture_resource_t rsc;
00311         memset(&rsc, 0, sizeof(rsc));
00312 #ifdef __APPLE__
00313         rsc.p_sys = malloc(sizeof(*rsc.p_sys));
00314         if (rsc.p_sys)
00315         {
00316             rsc.p_sys->vgl = vgl;
00317             rsc.p_sys->texture = &vgl->texture[i];
00318         }
00319 #endif
00320         rsc.p[0].p_pixels = vgl->buffer[i];
00321         rsc.p[0].i_pitch  = vgl->fmt.i_width * vgl->tex_pixel_size;
00322         rsc.p[0].i_lines  = vgl->fmt.i_height;
00323 
00324         picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
00325         if (!picture[i]) {
00326             free(vgl->buffer[i]);
00327             vgl->buffer[i] = NULL;
00328             break;
00329         }
00330     }
00331     if (i < VLCGL_TEXTURE_COUNT)
00332         goto error;
00333 
00334     /* */
00335     picture_pool_configuration_t cfg;
00336     memset(&cfg, 0, sizeof(cfg));
00337     cfg.picture_count = i;
00338     cfg.picture = picture;
00339 #ifdef __APPLE__
00340     cfg.lock = PictureLock;
00341     cfg.unlock = PictureUnlock;
00342 #endif
00343     vgl->pool = picture_pool_NewExtended(&cfg);
00344     if (!vgl->pool)
00345         goto error;
00346 
00347     vout_display_opengl_ResetTextures(vgl);
00348 
00349     return vgl->pool;
00350 
00351 error:
00352     for (int j = 0; j < i; j++) {
00353         picture_Delete(picture[j]);
00354         vgl->buffer[j] = NULL;
00355     }
00356     return NULL;
00357 }
00358 
00359 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
00360                                        picture_t *picture)
00361 {
00362     /* On Win32/GLX, we do this the usual way:
00363        + Fill the buffer with new content,
00364        + Reload the texture,
00365        + Use the texture.
00366 
00367        On OS X with VRAM or AGP texturing, the order has to be:
00368        + Reload the texture,
00369        + Fill the buffer with new content,
00370        + Use the texture.
00371 
00372        (Thanks to gcc from the Arstechnica forums for the tip)
00373 
00374        Therefore on OSX, we have to use two buffers and textures and use a
00375        lock(/unlock) managed picture pool.
00376      */
00377 
00378     if (vout_opengl_Lock(vgl->gl))
00379         return VLC_EGENERIC;
00380 
00381 #ifdef __APPLE__
00382     /* Bind to the texture for drawing */
00383     glBindTexture(VLCGL_TARGET, get_texture(picture));
00384 #else
00385     /* Update the texture */
00386     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
00387                     vgl->fmt.i_width, vgl->fmt.i_height,
00388                     VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
00389 #endif
00390 
00391     vout_opengl_Unlock(vgl->gl);
00392     return VLC_SUCCESS;
00393 }
00394 
00395 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
00396                                        const video_format_t *source)
00397 {
00398     if (vout_opengl_Lock(vgl->gl))
00399         return VLC_EGENERIC;
00400 
00401     /* glTexCoord works differently with GL_TEXTURE_2D and
00402        GL_TEXTURE_RECTANGLE_EXT */
00403 #if VLCGL_TARGET == GL_TEXTURE_2D
00404     const float f_normw = vgl->tex_width;
00405     const float f_normh = vgl->tex_height;
00406 #else
00407     assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
00408     const float f_normw = 1.0;
00409     const float f_normh = 1.0;
00410 #endif
00411 
00412     float f_x      = (source->i_x_offset +                       0 ) / f_normw;
00413     float f_y      = (source->i_y_offset +                       0 ) / f_normh;
00414     float f_width  = (source->i_x_offset + source->i_visible_width ) / f_normw;
00415     float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
00416 
00417     /* Why drawing here and not in Render()? Because this way, the
00418        OpenGL providers can call vout_display_opengl_Display to force redraw.i
00419        Currently, the OS X provider uses it to get a smooth window resizing */
00420 
00421     glClear(GL_COLOR_BUFFER_BIT);
00422 
00423     glEnable(VLCGL_TARGET);
00424 
00425     glBegin(GL_POLYGON);
00426     glTexCoord2f(f_x,      f_y);      glVertex2f(-1.0,  1.0);
00427     glTexCoord2f(f_width,  f_y);      glVertex2f( 1.0,  1.0);
00428     glTexCoord2f(f_width,  f_height); glVertex2f( 1.0, -1.0);
00429     glTexCoord2f(f_x,      f_height); glVertex2f(-1.0, -1.0);
00430     glEnd();
00431 
00432     glDisable(VLCGL_TARGET);
00433 
00434     vout_opengl_Swap(vgl->gl);
00435 
00436     vout_opengl_Unlock(vgl->gl);
00437     return VLC_SUCCESS;
00438 }
00439 

Generated on Tue May 25 08:05:00 2010 for VLC by  doxygen 1.5.6