X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_output%2Fopengl.c;h=d896e7ca3e52c75245cddae800f5f448b45ceaaa;hb=b76d7cf6283749d91448cb2b175f7cd9889fbe02;hp=95e1fa6af7c966cfdb80dddf073b6d9044880f60;hpb=77133bfce21583fe9fdbb55b6b6f15dbf4bccc4f;p=vlc diff --git a/modules/video_output/opengl.c b/modules/video_output/opengl.c index 95e1fa6af7..d896e7ca3e 100644 --- a/modules/video_output/opengl.c +++ b/modules/video_output/opengl.c @@ -1,11 +1,12 @@ /***************************************************************************** * opengl.c: OpenGL video output ***************************************************************************** - * Copyright (C) 2004 VideoLAN + * Copyright (C) 2004 the VideoLAN team * $Id$ * * Authors: Cyril Deguet * Gildas Bazin + * Eric Petit * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +20,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -32,7 +33,7 @@ #include #include -#ifdef SYS_DARWIN +#ifdef __APPLE__ #include #include @@ -63,9 +64,25 @@ #define VLCGL_RGB_FORMAT GL_RGBA #define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE +/* YUY2 */ +#ifndef YCBCR_MESA +#define YCBCR_MESA 0x8757 +#endif +#ifndef UNSIGNED_SHORT_8_8_MESA +#define UNSIGNED_SHORT_8_8_MESA 0x85BA +#endif +#define VLCGL_YUV_FORMAT YCBCR_MESA +#define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA + /* Use RGB on Win32/GLX */ #define VLCGL_FORMAT VLCGL_RGB_FORMAT #define VLCGL_TYPE VLCGL_RGB_TYPE +//#define VLCGL_FORMAT VLCGL_YUV_FORMAT +//#define VLCGL_TYPE VLCGL_YUV_TYPE +#endif + +#ifndef GL_CLAMP_TO_EDGE +# define GL_CLAMP_TO_EDGE 0x812F #endif /* OpenGL effects */ @@ -91,6 +108,10 @@ static int InitTextures( vout_thread_t * ); static int SendEvents( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +#define SPEED_TEXT N_( "OpenGL cube rotation speed" ) /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -108,18 +129,21 @@ static char *ppsz_effects_text[] = { N_("None"), N_("Cube"), N_("Transparent Cube") }; vlc_module_begin(); + set_shortname( "OpenGL" ); + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VOUT ); set_description( _("OpenGL video output") ); -#ifdef SYS_DARWIN - set_capability( "video output", 0 ); +#ifdef __APPLE__ + set_capability( "video output", 200 ); #else set_capability( "video output", 20 ); #endif add_shortcut( "opengl" ); add_float( "opengl-cube-speed", 2.0, NULL, SPEED_TEXT, - SPEED_LONGTEXT, VLC_FALSE ); + SPEED_LONGTEXT, VLC_TRUE ); set_callbacks( CreateVout, DestroyVout ); add_string( "opengl-effect", "none", NULL, EFFECT_TEXT, - EFFECT_LONGTEXT, VLC_TRUE ); + EFFECT_LONGTEXT, VLC_FALSE ); change_string_list( ppsz_effects, ppsz_effects_text, 0 ); vlc_module_end(); @@ -162,13 +186,14 @@ static int CreateVout( vlc_object_t *p_this ) var_Create( p_vout, "opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); -#ifdef SYS_DARWIN - p_sys->i_tex_width = p_vout->render.i_width; - p_sys->i_tex_height = p_vout->render.i_height; + p_sys->i_index = 0; +#ifdef __APPLE__ + p_sys->i_tex_width = p_vout->fmt_in.i_width; + p_sys->i_tex_height = p_vout->fmt_in.i_height; #else /* A texture must have a size aligned on a power of 2 */ - p_sys->i_tex_width = GetAlignedSize( p_vout->render.i_width ); - p_sys->i_tex_height = GetAlignedSize( p_vout->render.i_height ); + p_sys->i_tex_width = GetAlignedSize( p_vout->fmt_in.i_width ); + p_sys->i_tex_height = GetAlignedSize( p_vout->fmt_in.i_height ); #endif msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width, @@ -190,6 +215,8 @@ static int CreateVout( vlc_object_t *p_this ) p_sys->p_vout->render.i_width = p_vout->render.i_width; p_sys->p_vout->render.i_height = p_vout->render.i_height; p_sys->p_vout->render.i_aspect = p_vout->render.i_aspect; + p_sys->p_vout->fmt_render = p_vout->fmt_render; + p_sys->p_vout->fmt_in = p_vout->fmt_in; p_sys->p_vout->b_scale = p_vout->b_scale; p_sys->p_vout->i_alignment = p_vout->i_alignment; @@ -217,6 +244,7 @@ static int CreateVout( vlc_object_t *p_this ) var_Create( p_sys->p_vout, "mouse-y", VLC_VAR_INTEGER ); var_Create( p_sys->p_vout, "mouse-moved", VLC_VAR_BOOL ); var_Create( p_sys->p_vout, "mouse-clicked", VLC_VAR_INTEGER ); + var_Create( p_sys->p_vout, "mouse-button-down", VLC_VAR_INTEGER ); var_Create( p_sys->p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); @@ -224,6 +252,7 @@ static int CreateVout( vlc_object_t *p_this ) var_AddCallback( p_sys->p_vout, "mouse-y", SendEvents, p_vout ); var_AddCallback( p_sys->p_vout, "mouse-moved", SendEvents, p_vout ); var_AddCallback( p_sys->p_vout, "mouse-clicked", SendEvents, p_vout ); + var_AddCallback( p_sys->p_vout, "mouse-button-down", SendEvents, p_vout ); return VLC_SUCCESS; } @@ -239,15 +268,12 @@ static int Init( vout_thread_t *p_vout ) p_sys->p_vout->pf_init( p_sys->p_vout ); -#ifdef SYS_DARWIN +#if defined( __APPLE__ ) || (VLCGL_FORMAT == YCBCR_MESA) p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2'); - p_vout->output.i_rmask = 0x00ff0000; - p_vout->output.i_gmask = 0x0000ff00; - p_vout->output.i_bmask = 0x000000ff; i_pixel_pitch = 2; -#else -#if VLCGL_RGB_FORMAT == GL_RGB -# if VLCGL_RGB_TYPE == GL_UNSIGNED_BYTE + +#elif VLCGL_FORMAT == GL_RGB +# if VLCGL_TYPE == GL_UNSIGNED_BYTE p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); p_vout->output.i_rmask = 0x000000ff; p_vout->output.i_gmask = 0x0000ff00; @@ -266,7 +292,6 @@ static int Init( vout_thread_t *p_vout ) p_vout->output.i_gmask = 0x0000ff00; p_vout->output.i_bmask = 0x00ff0000; i_pixel_pitch = 4; -#endif #endif /* Since OpenGL can do rescaling for us, stick to the default @@ -275,6 +300,9 @@ static int Init( vout_thread_t *p_vout ) p_vout->output.i_height = p_vout->render.i_height; p_vout->output.i_aspect = p_vout->render.i_aspect; + p_vout->fmt_out = p_vout->fmt_in; + p_vout->fmt_out.i_chroma = p_vout->output.i_chroma; + /* We know the chroma, allocate one buffer which will be used * directly by the decoder */ p_sys->pp_buffer[0] = @@ -309,6 +337,13 @@ static int Init( vout_thread_t *p_vout ) I_OUTPUTPICTURES = 1; + if( p_sys->p_vout->pf_lock && + p_sys->p_vout->pf_lock( p_sys->p_vout ) ) + { + msg_Warn( p_vout, "could not lock OpenGL provider" ); + return 0; + } + InitTextures( p_vout ); glDisable(GL_BLEND); @@ -358,6 +393,11 @@ static int Init( vout_thread_t *p_vout ) glTranslatef( 0.0, 0.0, - 5.0 ); } + if( p_sys->p_vout->pf_unlock ) + { + p_sys->p_vout->pf_unlock( p_sys->p_vout ); + } + return 0; } @@ -366,8 +406,22 @@ static int Init( vout_thread_t *p_vout ) *****************************************************************************/ static void End( vout_thread_t *p_vout ) { + vout_sys_t *p_sys = p_vout->p_sys; + + if( p_sys->p_vout->pf_lock && + p_sys->p_vout->pf_lock( p_sys->p_vout ) ) + { + msg_Warn( p_vout, "could not lock OpenGL provider" ); + return; + } + glFinish(); glFlush(); + + if( p_sys->p_vout->pf_unlock ) + { + p_sys->p_vout->pf_unlock( p_sys->p_vout ); + } } /***************************************************************************** @@ -404,11 +458,34 @@ static int Manage( vout_thread_t *p_vout ) i_fullscreen_change = ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE ); + p_vout->fmt_out.i_x_offset = p_sys->p_vout->fmt_in.i_x_offset = + p_vout->fmt_in.i_x_offset; + p_vout->fmt_out.i_y_offset = p_sys->p_vout->fmt_in.i_y_offset = + p_vout->fmt_in.i_y_offset; + p_vout->fmt_out.i_visible_width = p_sys->p_vout->fmt_in.i_visible_width = + p_vout->fmt_in.i_visible_width; + p_vout->fmt_out.i_visible_height = p_sys->p_vout->fmt_in.i_visible_height = + p_vout->fmt_in.i_visible_height; + p_vout->fmt_out.i_aspect = p_sys->p_vout->fmt_in.i_aspect = + p_vout->fmt_in.i_aspect; + p_vout->fmt_out.i_sar_num = p_sys->p_vout->fmt_in.i_sar_num = + p_vout->fmt_in.i_sar_num; + p_vout->fmt_out.i_sar_den = p_sys->p_vout->fmt_in.i_sar_den = + p_vout->fmt_in.i_sar_den; + p_vout->output.i_aspect = p_vout->fmt_in.i_aspect; + p_sys->p_vout->i_changes = p_vout->i_changes; i_ret = p_sys->p_vout->pf_manage( p_sys->p_vout ); p_vout->i_changes = p_sys->p_vout->i_changes; -#ifdef SYS_DARWIN +#ifdef __APPLE__ + if( p_sys->p_vout->pf_lock && + p_sys->p_vout->pf_lock( p_sys->p_vout ) ) + { + msg_Warn( p_vout, "could not lock OpenGL provider" ); + return i_ret; + } + /* On OS X, we create the window and the GL view when entering fullscreen - the textures have to be inited again */ if( i_fullscreen_change ) @@ -440,6 +517,11 @@ static int Manage( vout_thread_t *p_vout ) glTranslatef( 0.0, 0.0, - 5.0 ); } } + + if( p_sys->p_vout->pf_unlock ) + { + p_sys->p_vout->pf_unlock( p_sys->p_vout ); + } #endif return i_ret; @@ -451,19 +533,6 @@ static int Manage( vout_thread_t *p_vout ) static void Render( vout_thread_t *p_vout, picture_t *p_pic ) { vout_sys_t *p_sys = p_vout->p_sys; - float f_width, f_height; - - /* glTexCoord works differently with GL_TEXTURE_2D and - GL_TEXTURE_RECTANGLE_EXT */ -#ifdef SYS_DARWIN - f_width = (float)p_vout->output.i_width; - f_height = (float)p_vout->output.i_height; -#else - f_width = (float)p_vout->output.i_width / p_sys->i_tex_width; - f_height = (float)p_vout->output.i_height / p_sys->i_tex_height; -#endif - - glClear( GL_COLOR_BUFFER_BIT ); /* On Win32/GLX, we do this the usual way: + Fill the buffer with new content, @@ -482,24 +551,93 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic ) OS X, we first render, then reload the texture to be used next time. */ -#ifdef SYS_DARWIN + if( p_sys->p_vout->pf_lock && + p_sys->p_vout->pf_lock( p_sys->p_vout ) ) + { + msg_Warn( p_vout, "could not lock OpenGL provider" ); + return; + } + +#ifdef __APPLE__ + int i_new_index; + i_new_index = ( p_sys->i_index + 1 ) & 1; + + + /* Update the texture */ + glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_new_index] ); + glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, + p_vout->fmt_out.i_width, + p_vout->fmt_out.i_height, + VLCGL_FORMAT, VLCGL_TYPE, p_sys->pp_buffer[i_new_index] ); + + /* Bind to the previous texture for drawing */ glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] ); -#else + /* Switch buffers */ + p_sys->i_index = i_new_index; + p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index]; + +#else /* Update the texture */ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, - p_vout->render.i_width, p_vout->render.i_height, - VLCGL_RGB_FORMAT, VLCGL_RGB_TYPE, p_sys->pp_buffer[0] ); + p_vout->fmt_out.i_width, + p_vout->fmt_out.i_height, + VLCGL_FORMAT, VLCGL_TYPE, p_sys->pp_buffer[0] ); #endif + if( p_sys->p_vout->pf_unlock ) + { + p_sys->p_vout->pf_unlock( p_sys->p_vout ); + } +} + +/***************************************************************************** + * DisplayVideo: displays previously rendered output + *****************************************************************************/ +static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vout_sys_t *p_sys = p_vout->p_sys; + float f_width, f_height, f_x, f_y; + + if( p_sys->p_vout->pf_lock && + p_sys->p_vout->pf_lock( p_sys->p_vout ) ) + { + msg_Warn( p_vout, "could not lock OpenGL provider" ); + return; + } + + /* glTexCoord works differently with GL_TEXTURE_2D and + GL_TEXTURE_RECTANGLE_EXT */ +#ifdef __APPLE__ + f_x = (float)p_vout->fmt_out.i_x_offset; + f_y = (float)p_vout->fmt_out.i_y_offset; + f_width = (float)p_vout->fmt_out.i_x_offset + + (float)p_vout->fmt_out.i_visible_width; + f_height = (float)p_vout->fmt_out.i_y_offset + + (float)p_vout->fmt_out.i_visible_height; +#else + f_x = (float)p_vout->fmt_out.i_x_offset / p_sys->i_tex_width; + f_y = (float)p_vout->fmt_out.i_y_offset / p_sys->i_tex_height; + f_width = ( (float)p_vout->fmt_out.i_x_offset + + p_vout->fmt_out.i_visible_width ) / p_sys->i_tex_width; + f_height = ( (float)p_vout->fmt_out.i_y_offset + + p_vout->fmt_out.i_visible_height ) / p_sys->i_tex_height; +#endif + + /* Why drawing here and not in Render()? Because this way, the + OpenGL providers can call pf_display to force redraw. Currently, + the OS X provider uses it to get a smooth window resizing */ + + glClear( GL_COLOR_BUFFER_BIT ); + if( p_sys->i_effect == OPENGL_EFFECT_NONE ) { glEnable( VLCGL_TARGET ); glBegin( GL_POLYGON ); - glTexCoord2f( 0.0, 0.0 ); glVertex2f( -1.0, 1.0 ); - glTexCoord2f( f_width, 0.0 ); glVertex2f( 1.0, 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex2f( -1.0, 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex2f( 1.0, 1.0 ); glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 ); - glTexCoord2f( 0.0, f_height ); glVertex2f( -1.0, -1.0 ); + glTexCoord2f( f_x, f_height ); glVertex2f( -1.0, -1.0 ); glEnd(); } else @@ -510,65 +648,51 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic ) glBegin( GL_QUADS ); /* Front */ - glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, 1.0 ); - glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, 1.0, 1.0 ); + glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 ); glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, 1.0 ); - glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, 1.0, 1.0 ); /* Left */ - glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 ); - glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, 1.0, - 1.0 ); + glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 ); glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, 1.0 ); - glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex3f( - 1.0, 1.0, 1.0 ); /* Back */ - glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, - 1.0 ); - glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex3f( 1.0, 1.0, - 1.0 ); + glTexCoord2f( f_x, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 ); glTexCoord2f( f_width, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 ); - glTexCoord2f( f_width, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex3f( - 1.0, 1.0, - 1.0 ); /* Right */ - glTexCoord2f( 0, 0 ); glVertex3f( 1.0, 1.0, 1.0 ); - glTexCoord2f( 0, f_height ); glVertex3f( 1.0, - 1.0, 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex3f( 1.0, 1.0, 1.0 ); + glTexCoord2f( f_x, f_height ); glVertex3f( 1.0, - 1.0, 1.0 ); glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 ); - glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, 1.0, - 1.0 ); /* Top */ - glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, 1.0, - 1.0 ); - glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, 1.0, 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, 1.0, - 1.0 ); + glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, 1.0, 1.0 ); glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, 1.0, 1.0 ); - glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, 1.0, - 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, 1.0, - 1.0 ); /* Bottom */ - glTexCoord2f( 0, 0 ); glVertex3f( - 1.0, - 1.0, 1.0 ); - glTexCoord2f( 0, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 ); + glTexCoord2f( f_x, f_y ); glVertex3f( - 1.0, - 1.0, 1.0 ); + glTexCoord2f( f_x, f_height ); glVertex3f( - 1.0, - 1.0, - 1.0 ); glTexCoord2f( f_width, f_height ); glVertex3f( 1.0, - 1.0, - 1.0 ); - glTexCoord2f( f_width, 0 ); glVertex3f( 1.0, - 1.0, 1.0 ); + glTexCoord2f( f_width, f_y ); glVertex3f( 1.0, - 1.0, 1.0 ); glEnd(); } glDisable( VLCGL_TARGET ); -#ifdef SYS_DARWIN - /* Switch buffers */ - p_sys->i_index = ( p_sys->i_index + 1 ) & 1; - p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index]; - - /* Update the texture */ - glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] ); - glTexSubImage2D( VLCGL_TARGET, 0, 0, 0, p_sys->i_tex_width, - p_sys->i_tex_height, VLCGL_FORMAT, VLCGL_TYPE, - p_sys->pp_buffer[p_sys->i_index] ); -#endif -} - -/***************************************************************************** - * DisplayVideo: displays previously rendered output - *****************************************************************************/ -static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic ) -{ - vout_sys_t *p_sys = p_vout->p_sys; p_sys->p_vout->pf_swap( p_sys->p_vout ); + + if( p_sys->p_vout->pf_unlock ) + { + p_sys->p_vout->pf_unlock( p_sys->p_vout ); + } } int GetAlignedSize( int i_size ) @@ -589,10 +713,17 @@ static int Control( vout_thread_t *p_vout, int i_query, va_list args ) { vout_sys_t *p_sys = p_vout->p_sys; - if( p_sys->p_vout->pf_control ) - return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args ); - else + switch( i_query ) + { + case VOUT_SNAPSHOT: return vout_vaControlDefault( p_vout, i_query, args ); + + default: + if( p_sys->p_vout->pf_control ) + return p_sys->p_vout->pf_control( p_sys->p_vout, i_query, args ); + else + return vout_vaControlDefault( p_vout, i_query, args ); + } } static int InitTextures( vout_thread_t *p_vout ) @@ -610,15 +741,15 @@ static int InitTextures( vout_thread_t *p_vout ) /* Set the texture parameters */ glTexParameterf( VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0 ); + glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - - glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP ); - glTexParameteri( VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP ); - + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); -#ifdef SYS_DARWIN +#ifdef __APPLE__ /* Tell the driver not to make a copy of the texture but to use our buffer */ glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );