/* =========================================================================== Wolfenstein: Enemy Territory GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). Wolf ET Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Wolf ET Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wolf ET Source Code. If not, see . In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include Q3DEF_BEGIN #include "../renderer/tr_local.h" #include "../client/client.h" #include "mac_local.h" Q3DEF_END #include "mac_glimp.h" #include "CDrawSprocket.h" #include "MacPrefs.h" #include "AGLUtils.h" #include "PickMonitor/pickmonitor.h" //#define PTHREADS 1 #define MPLIB 1 #if PTHREADS #include #include #endif extern long gSystemVersion; // Mac-specific rendering cvars cvar_t *r_device; cvar_t *r_ext_transform_hint; #if !MAC_WOLF cvar_t *r_ati_fsaa_samples; #endif cvar_t *r_nv_fsaa_samples; cvar_t *r_nv_quincunx; #if MAC_MOHAA cvar_t *r_stipplelines; #endif #if MAC_Q3_OLDSTUFF // Newer Q3 variables that aren't in the older Q3 cores cvar_t *r_ext_preferred_tc_method; #if !MAC_WOLF cvar_t *r_ext_texture_filter_anisotropic; #endif #endif #if MAC_JKJA && MAC_Q3_MP // Hack variable for deciding which kind of texture rectangle thing to do (for some // reason it acts different on radeon! It's against the spec!). bool g_bTextureRectangleHack = false; #endif #if MAC_WOLF #define TC_S3TC_DXT TC_EXT_COMP_S3TC int gl_NormalFontBase = 0; #endif #if !MAC_JK2 && !MAC_JK2_MP glHardwareType_t sys_hardwareType; #endif aglstate_t sys_gl; glconfig_mac_t glConfig_Mac; int gClampMode = GL_CLAMP_TO_EDGE; int vid_xpos; int vid_ypos; static Boolean sHasGameGamma = false; qboolean GLimp_ChangeMode( int mode ); void GLimp_EndFrame( void ); static void GLimp_Extensions( void ); #pragma mark ¥ Mac-Specific Calls /* ============ CheckErrors ============ */ void CheckErrors( const char *inFile, int inLine ) { GLenum err; err = aglGetError(); if ( err != AGL_NO_ERROR ) { Com_Printf( "aglGetError: %s\n", aglErrorString( err ) ); Com_Error( ERR_FATAL, "aglGetError: %s at %s line %d", aglErrorString( err ), inFile, inLine ); } } Boolean IsRadeon( void ) { if ( strstr( glConfig.renderer_string, "Radeon" ) || strstr( glConfig.renderer_string, "R-200" ) ) { return true; } else { return false; } } #if !MAC_JKJA || MAC_Q3_MP /* ================ VID_Printf DLL glue ================ */ #define MAXPRINTMSG 4096 void VID_Printf( int print_level, const char *fmt, ... ) { va_list argptr; char msg[MAXPRINTMSG]; va_start( argptr,fmt ); vsprintf( msg,fmt,argptr ); va_end( argptr ); if ( print_level == PRINT_ALL ) { Com_Printf( "%s", msg ); } else if ( print_level == PRINT_WARNING ) { Com_Printf( S_COLOR_YELLOW "%s", msg ); // yellow } else if ( print_level == PRINT_DEVELOPER ) { Com_DPrintf( S_COLOR_RED "%s", msg ); } } #endif void GLimp_InitGamma( void ) { // if we're already set up, bail if ( sys_gl.systemGammas ) { return; } { if ( 1 ) { CGDisplayErr cgErr; CGTableCount count = 0; CGGammaTable *cgGamma; cgGamma = (CGGammaTable *)malloc( sizeof( CGGammaTable ) ); if ( !cgGamma ) { Com_Error( ERR_FATAL, "Could not initialize gamma table" ); } cgErr = CGGetDisplayTransferByTable( NULL, 256, cgGamma->red, cgGamma->green, cgGamma->blue, &count ); if ( cgErr ) { Com_Error( ERR_FATAL, "Could not read gamma table" ); } glConfig.deviceSupportsGamma = qtrue; sys_gl.systemGammas = (Ptr) cgGamma; VID_Printf( PRINT_ALL, "CoreGraphics gamma table loaded\n" ); } else { glConfig.deviceSupportsGamma = qfalse; sys_gl.systemGammas = NULL; VID_Printf( PRINT_ALL, "Could not call CoreGraphics gamma table API\n" ); } } } //======================================================================= /* ===================== GLimp_ChangeDisplay ===================== */ void GLimp_ChangeDisplay( int *actualWidth, int *actualHeight ) { } //======================================================================= /* =================== GLimp_AglDescribe_f =================== */ void GLimp_AglDescribe_f( void ) { long value; long r,g,b,a; long stencil, depth; VID_Printf( PRINT_ALL, "Selected pixel format 0x%x\n", (int)sys_gl.fmt ); VID_Printf( PRINT_ALL, "TEXTURE_MEMORY: %i\n", sys_gl.textureMemory ); VID_Printf( PRINT_ALL, "VIDEO_MEMORY: %i\n", sys_gl.videoMemory ); aglDescribePixelFormat( sys_gl.fmt, AGL_RED_SIZE, &r ); aglDescribePixelFormat( sys_gl.fmt, AGL_GREEN_SIZE, &g ); aglDescribePixelFormat( sys_gl.fmt, AGL_BLUE_SIZE, &b ); aglDescribePixelFormat( sys_gl.fmt, AGL_ALPHA_SIZE, &a ); aglDescribePixelFormat( sys_gl.fmt, AGL_STENCIL_SIZE, &stencil ); aglDescribePixelFormat( sys_gl.fmt, AGL_DEPTH_SIZE, &depth ); VID_Printf( PRINT_ALL, "red:%i green:%i blue:%i alpha:%i depth:%i stencil:%i\n", r, g, b, a, depth, stencil ); aglDescribePixelFormat( sys_gl.fmt, AGL_BUFFER_SIZE, &value ); VID_Printf( PRINT_ALL, "BUFFER_SIZE: %i\n", value ); aglDescribePixelFormat( sys_gl.fmt, AGL_PIXEL_SIZE, &value ); VID_Printf( PRINT_ALL, "PIXEL_SIZE: %i\n", value ); aglDescribePixelFormat( sys_gl.fmt, AGL_RENDERER_ID, &value ); VID_Printf( PRINT_ALL, "RENDERER_ID: %i\n", value ); } /* =================== GLimp_AglState_f =================== */ void GLimp_AglState_f( void ) { char *cmd; int state, value; if ( Cmd_Argc() != 3 ) { VID_Printf( PRINT_ALL, "Usage: aglstate <0/1>\n" ); return; } cmd = Cmd_Argv( 1 ); if ( !Q_stricmp( cmd, "rasterization" ) ) { state = AGL_RASTERIZATION; } else { VID_Printf( PRINT_ALL, "Unknown agl state: %s\n", cmd ); return; } cmd = Cmd_Argv( 2 ); value = atoi( cmd ); if ( value ) { aglEnable( sys_gl.context, state ); } else { aglDisable( sys_gl.context, state ); } } Q3DEF void GLimp_pause( void ) { _aglSuspendGameContext(); } Q3DEF void GLimp_resume( void ) { _aglResumeGameContext(); } #if TARGET_API_MAC_CARBON Q3DEF void GLimp_SetGameGamma( void ) { if ( ( gSystemVersion < 0x1000 ) || ( !sHasGameGamma ) ) { return; } { CGDisplayErr cgErr; cgErr = CGSetDisplayTransferByTable( NULL, 256, sys_gl.gameGamma.red, sys_gl.gameGamma.green, sys_gl.gameGamma.blue ); #if _DEBUG VID_Printf( PRINT_ALL, "CoreGraphics gamma table set, err: %d\n", cgErr ); #endif } } #endif static void GLW_InitTextureCompression( void ) { qboolean newer_tc, old_tc; // Check for available tc methods. newer_tc = ( strstr( glConfig.extensions_string, "ARB_texture_compression" ) && strstr( glConfig.extensions_string, "EXT_texture_compression_s3tc" ) ) ? qtrue : qfalse; old_tc = ( strstr( glConfig.extensions_string, "GL_S3_s3tc" ) ) ? qtrue : qfalse; if ( old_tc ) { VID_Printf( PRINT_ALL, "...GL_S3_s3tc available\n" ); } if ( newer_tc ) { VID_Printf( PRINT_ALL, "...GL_EXT_texture_compression_s3tc available\n" ); } #if MAC_MOHAA || MAC_WOLF if ( !r_ext_compressed_textures->integer ) #else if ( !r_ext_compressed_textures->value ) #endif { // Compressed textures are off glConfig.textureCompression = TC_NONE; VID_Printf( PRINT_ALL, "...ignoring texture compression\n" ); } else if ( !old_tc && !newer_tc ) { { // Requesting texture compression, but no method found glConfig.textureCompression = TC_NONE; VID_Printf( PRINT_ALL, "...no supported texture compression method found\n" ); VID_Printf( PRINT_ALL, ".....ignoring texture compression\n" ); } } else { #if MAC_MOHAA qglCompressedTexImage2DARB = glCompressedTexImage2DARB; #endif // some form of supported texture compression is avaiable, so see if the user has a preference if ( r_ext_preferred_tc_method->integer == TC_NONE ) { // No preference, so pick the best if ( newer_tc ) { VID_Printf( PRINT_ALL, "...no tc preference specified\n" ); VID_Printf( PRINT_ALL, ".....using GL_EXT_texture_compression_s3tc\n" ); glConfig.textureCompression = TC_S3TC_DXT; } else { VID_Printf( PRINT_ALL, "...no tc preference specified\n" ); VID_Printf( PRINT_ALL, ".....using GL_S3_s3tc\n" ); glConfig.textureCompression = TC_S3TC; } } else { // User has specified a preference, now see if this request can be honored if ( old_tc && newer_tc ) { // both are avaiable, so we can use the desired tc method if ( r_ext_preferred_tc_method->integer == TC_S3TC ) { VID_Printf( PRINT_ALL, "...using preferred tc method, GL_S3_s3tc\n" ); glConfig.textureCompression = TC_S3TC; } else { VID_Printf( PRINT_ALL, "...using preferred tc method, GL_EXT_texture_compression_s3tc\n" ); glConfig.textureCompression = TC_S3TC_DXT; } } else { // Both methods are not available, so this gets trickier if ( r_ext_preferred_tc_method->integer == TC_S3TC ) { // Preferring to user older compression if ( old_tc ) { VID_Printf( PRINT_ALL, "...using GL_S3_s3tc\n" ); glConfig.textureCompression = TC_S3TC; } else { // Drat, preference can't be honored VID_Printf( PRINT_ALL, "...preferred tc method, GL_S3_s3tc not available\n" ); VID_Printf( PRINT_ALL, ".....falling back to GL_EXT_texture_compression_s3tc\n" ); glConfig.textureCompression = TC_S3TC_DXT; } } else { // Preferring to user newer compression if ( newer_tc ) { VID_Printf( PRINT_ALL, "...using GL_EXT_texture_compression_s3tc\n" ); glConfig.textureCompression = TC_S3TC_DXT; } else { // Drat, preference can't be honored VID_Printf( PRINT_ALL, "...preferred tc method, GL_EXT_texture_compression_s3tc not available\n" ); VID_Printf( PRINT_ALL, ".....falling back to GL_S3_s3tc\n" ); glConfig.textureCompression = TC_S3TC; } } } } } } /* =================== GLimp_Extensions =================== */ static void GLimp_Extensions( void ) { if ( !r_allowExtensions->integer ) { VID_Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" ); return; } VID_Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); // Select our tc scheme GLW_InitTextureCompression(); // GL_EXT_texture_env_add glConfig.textureEnvAddAvailable = qfalse; if ( strstr( glConfig.extensions_string, "EXT_texture_env_add" ) ) { if ( r_ext_texture_env_add->integer ) { glConfig.textureEnvAddAvailable = qtrue; VID_Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); } else { glConfig.textureEnvAddAvailable = qfalse; VID_Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); } } else { VID_Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" ); } // GL_EXT_texture_filter_anisotropic #if MAC_Q3_OLDSTUFF #if MAC_WOLF glConfig.anisotropicAvailable = qfalse; #else glConfig_Mac.textureFilterAnisotropicAvailable = qfalse; #endif #else #if MAC_JKJA glConfig.maxTextureFilterAnisotropy = 0; #else glConfig.textureFilterAnisotropicAvailable = qfalse; #endif #endif if ( strstr( glConfig.extensions_string, "EXT_texture_filter_anisotropic" ) ) { #if MAC_Q3_OLDSTUFF #if MAC_WOLF glConfig.anisotropicAvailable = qtrue; #else glConfig_Mac.textureFilterAnisotropicAvailable = qtrue; #endif #else #if MAC_JKJA qglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.maxTextureFilterAnisotropy ); #else glConfig.textureFilterAnisotropicAvailable = qtrue; #endif #endif VID_Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic available\n" ); if ( r_ext_texture_filter_anisotropic->integer ) { VID_Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic\n" ); } else { VID_Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); } #if MAC_JKJA Cvar_Set( "r_ext_texture_filter_anisotropic_avail", va( "%f",glConfig.maxTextureFilterAnisotropy ) ); if ( r_ext_texture_filter_anisotropic->value > glConfig.maxTextureFilterAnisotropy ) { Cvar_Set( "r_ext_texture_filter_anisotropic", va( "%f",glConfig.maxTextureFilterAnisotropy ) ); } #else Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "1" ); #endif } else { VID_Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" ); Cvar_Set( "r_ext_texture_filter_anisotropic_avail", "0" ); } // GL_EXT_clamp_to_edge / GL_SGIS_clamp_to_edge #if MAC_STVEF_HM || MAC_Q3_OLDSTUFF gClampMode = GL_CLAMP; glConfig_Mac.clampToEdgeAvailable = qfalse; if ( strstr( glConfig.extensions_string, "GL_EXT_texture_edge_clamp" ) || strstr( glConfig.extensions_string, "GL_SGIS_texture_edge_clamp" ) ) { gClampMode = GL_CLAMP_TO_EDGE; glConfig_Mac.clampToEdgeAvailable = qtrue; VID_Printf( PRINT_ALL, "...Using GL_EXT_texture_edge_clamp\n" ); } #else glConfig.clampToEdgeAvailable = qfalse; if ( strstr( glConfig.extensions_string, "GL_EXT_texture_edge_clamp" ) || strstr( glConfig.extensions_string, "GL_SGIS_texture_edge_clamp" ) ) { glConfig.clampToEdgeAvailable = qtrue; VID_Printf( PRINT_ALL, "...Using texture_edge_clamp\n" ); } #endif // GL_ARB_multitexture qglMultiTexCoord2fARB = NULL; qglActiveTextureARB = NULL; qglClientActiveTextureARB = NULL; if ( strstr( glConfig.extensions_string, "GL_ARB_multitexture" ) ) { if ( r_ext_multitexture->integer ) { qglMultiTexCoord2fARB = glMultiTexCoord2fARB; qglActiveTextureARB = glActiveTextureARB; qglClientActiveTextureARB = glClientActiveTextureARB; if ( qglActiveTextureARB ) { qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, (long *)&glConfig.maxActiveTextures ); if ( glConfig.maxActiveTextures > 1 ) { VID_Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); } else { qglMultiTexCoord2fARB = NULL; qglActiveTextureARB = NULL; qglClientActiveTextureARB = NULL; VID_Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" ); } } } else { VID_Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); } } else { VID_Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); } // GL_EXT_compiled_vertex_array qglLockArraysEXT = NULL; qglUnlockArraysEXT = NULL; if ( strstr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) ) { if ( r_ext_compiled_vertex_array->integer ) { qglLockArraysEXT = glLockArraysEXT; qglUnlockArraysEXT = glUnlockArraysEXT; VID_Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); } else { VID_Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); } } else { VID_Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); } #if !MAC_Q3_OLDSTUFF // GL_ARB_point_parameters (10.2 and up) // LBO - the PC JK2 code checks for GL_EXT_point_parameters, but Apple supports the ARB version // which is identical. qglPointParameterfEXT = NULL; qglPointParameterfvEXT = NULL; if ( strstr( glConfig.extensions_string, "GL_ARB_point_parameters" ) ) { VID_Printf( PRINT_ALL, "...using GL_ARB_point_parameters\n" ); qglPointParameterfEXT = glPointParameterfARB; qglPointParameterfvEXT = glPointParameterfvARB; } else { VID_Printf( PRINT_ALL, "...GL_ARB_point_parameters not found\n" ); } #endif // GL_ARB_multisample if ( strstr( glConfig.extensions_string, "GL_ARB_multisample" ) || strstr( glConfig.extensions_string, "GL_multisample" ) ) { VID_Printf( PRINT_ALL, "...using GL_ARB_multisample\n" ); glConfig_Mac.multisampleAvailable = qtrue; } else { VID_Printf( PRINT_ALL, "...GL_ARB_multisample not found\n" ); glConfig_Mac.multisampleAvailable = qfalse; } // apple transform hint if ( strstr( glConfig.extensions_string, "GL_APPLE_transform_hint" ) ) { if ( r_ext_transform_hint->integer ) { glHint( GL_TRANSFORM_HINT_APPLE, GL_FASTEST ); VID_Printf( PRINT_ALL, "...using GL_APPLE_transform_hint\n" ); } else { VID_Printf( PRINT_ALL, "...ignoring GL_APPLE_transform_hint\n" ); } } else { VID_Printf( PRINT_ALL, "...GL_APPLE_transform_hint not found\n" ); } // APPLE_client_storage if ( strstr( glConfig.extensions_string, "GL_APPLE_client_storage" ) ) { glConfig_Mac.hasClientStorage = qtrue; VID_Printf( PRINT_ALL, "...using GL_APPLE_client_storage\n" ); } else { glConfig_Mac.hasClientStorage = qfalse; VID_Printf( PRINT_ALL, "...GL_APPLE_client_storage not found\n" ); } #if MAC_WOLF #if !MAC_WOLF_ET // GL_ATI_pn_triangles - ATI PN-Triangles if ( strstr( glConfig.extensions_string, "GL_ATIX_pn_triangles" ) ) { if ( r_ext_ATI_pntriangles->integer ) { ri.Printf( PRINT_ALL, "...using GL_ATI_pn_triangles\n" ); qglPNTrianglesiATI = glPNTrianglesiATIX; qglPNTrianglesfATI = glPNTrianglesfATIX; if ( !qglPNTrianglesiATI || !qglPNTrianglesfATI ) { ri.Error( ERR_FATAL, "bad getprocaddress 0" ); } } else { ri.Printf( PRINT_ALL, "...ignoring GL_ATI_pn_triangles\n" ); ri.Cvar_Set( "r_ext_ATI_pntriangles", "0" ); } } else { ri.Printf( PRINT_ALL, "...GL_ATI_pn_triangles not found\n" ); ri.Cvar_Set( "r_ext_ATI_pntriangles", "0" ); } #endif // GL_NV_fog_distance if ( strstr( glConfig.extensions_string, "GL_NV_fog_distance" ) ) { if ( r_ext_NV_fog_dist->integer ) { glConfig.NVFogAvailable = qtrue; ri.Printf( PRINT_ALL, "...using GL_NV_fog_distance\n" ); } else { ri.Printf( PRINT_ALL, "...ignoring GL_NV_fog_distance\n" ); ri.Cvar_Set( "r_ext_NV_fog_dist", "0" ); } } else { glConfig.NVFogAvailable = qfalse; ri.Printf( PRINT_ALL, "...GL_NV_fog_distance not found\n" ); ri.Cvar_Set( "r_ext_NV_fog_dist", "0" ); } #endif #if !MAC_JK2 && !MAC_JK2_MP & !MAC_MOHAA // determine ragePro status for rendering hacks if ( strstr( glConfig.renderer_string, "Rage Pro" ) || strstr( glConfig.renderer_string, "rage pro" ) ) { glConfig.hardwareType = GLHW_RAGEPRO; } else { glConfig.hardwareType = GLHW_GENERIC; } #endif // Enable FSAA on ATi cards if ( IsRadeon() /*&& gSystemVersion >= 0x0922*/ ) { GLenum err; aglSetInteger( sys_gl.context, ATI_FSAA_SAMPLES, (const long*)&r_ati_fsaa_samples->integer ); // Flush AGL_BAD_ENUM, which can occur if we're using older OS/drivers err = aglGetError(); } } void GLimp_SetFSAA( void ) { if ( !glConfig_Mac.multisampleAvailable ) { return; } VID_Printf( PRINT_ALL, "Changing nVidia FSAA samples to %d\n", r_nv_fsaa_samples->integer ); VID_Printf( PRINT_ALL, "...r_nv_quincunx is %d\n", r_nv_quincunx->integer ); switch ( r_nv_fsaa_samples->integer ) { case 0: case 1: glDisable( GL_MULTISAMPLE ); break; case 2: // 2 pixel, 2 tap case 4: // 4 pixel, 4 tap glEnable( GL_MULTISAMPLE ); if ( r_nv_quincunx->integer ) { glHint( GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST ); } else { glHint( GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST ); } break; } } #pragma mark - #pragma mark ¥ Q3 core calls extern "C" { void PlayIntroMovies( void ); } /* =================== GLimp_Init Don't return unless OpenGL has been properly initialized =================== */ Q3DEF void GLimp_Init( void ) { char buf[MAX_STRING_CHARS]; GLint major, minor; static qboolean registered; cvar_t *lastValidRenderer = Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE ); gDrawSprocket = new CDrawSprocket( macPrefs.displayID ); if ( gDrawSprocket == NULL ) { ExitToShell(); } VID_Printf( PRINT_ALL, "Initializing OpenGL subsystem\n" ); aglResetLibrary(); aglGetVersion( &major, &minor ); QGL_Init(); VID_Printf( PRINT_ALL, "aglVersion: %i.%i\n", (int)major, (int)minor ); r_device = Cvar_Get( "r_device", "0", CVAR_LATCH | CVAR_ARCHIVE ); r_ext_transform_hint = Cvar_Get( "r_ext_transform_hint", "1", CVAR_LATCH | CVAR_ARCHIVE ); // Default FSAA options to off r_ati_fsaa_samples = Cvar_Get( "r_ati_fsaa_samples", "1", CVAR_ARCHIVE ); r_nv_fsaa_samples = Cvar_Get( "r_nv_fsaa_samples", "0", CVAR_LATCH | CVAR_ARCHIVE ); r_nv_quincunx = Cvar_Get( "r_nv_quincunx", "0", CVAR_ARCHIVE ); #if MAC_Q3_OLDSTUFF r_ext_preferred_tc_method = Cvar_Get( "r_ext_preferred_tc_method", "0", CVAR_ARCHIVE | CVAR_LATCH ); r_ext_texture_filter_anisotropic = Cvar_Get( "r_ext_texture_filter_anisotropic", "0", CVAR_ARCHIVE ); #endif if ( !registered ) { Cmd_AddCommand( "aglDescribe", GLimp_AglDescribe_f ); Cmd_AddCommand( "aglState", GLimp_AglState_f ); } memset( &glConfig, 0, sizeof( glConfig ) ); memset( &glConfig_Mac, 0, sizeof( glConfig_Mac ) ); r_swapInterval->modified = qtrue; // force a set next frame // Get the display ID in case it isn't zero sys_gl.displayID = macPrefs.displayID = gDrawSprocket->GetDisplayID(); GLimp_InitGamma(); GLimp_ChangeMode( r_mode->integer ); // Get the display ID in case it was zero before and the user picked a monitor sys_gl.displayID = macPrefs.displayID = gDrawSprocket->GetDisplayID(); CheckErrors( __FILE__, __LINE__ ); // get our config strings #if MAC_JK2 || MAC_JKJA // LBO - starting with JK2, these are pointers to const strings glConfig.vendor_string = (const char *) qglGetString( GL_VENDOR ); glConfig.renderer_string = (const char *) qglGetString( GL_RENDERER ); glConfig.version_string = (const char *) qglGetString( GL_VERSION ); glConfig.extensions_string = (const char *) qglGetString( GL_EXTENSIONS ); #else Q_strncpyz( glConfig.vendor_string, (const char *)qglGetString( GL_VENDOR ), sizeof( glConfig.vendor_string ) ); Q_strncpyz( glConfig.renderer_string, (const char *)qglGetString( GL_RENDERER ), sizeof( glConfig.renderer_string ) ); Q_strncpyz( glConfig.version_string, (const char *)qglGetString( GL_VERSION ), sizeof( glConfig.version_string ) ); Q_strncpyz( glConfig.extensions_string, (const char *)qglGetString( GL_EXTENSIONS ), sizeof( glConfig.extensions_string ) ); #endif qglGetIntegerv( GL_MAX_TEXTURE_SIZE, (long *)&glConfig.maxTextureSize ); // stubbed or broken drivers may have reported 0... if ( glConfig.maxTextureSize <= 0 ) { glConfig.maxTextureSize = 0; } //GLimp_pause (); //aglSetDrawable (gAGLContext, NULL); //PlayIntroMovies (); //ExitToShell(); // // chipset specific configuration // strcpy( buf, glConfig.renderer_string ); strlwr( buf ); // // NOTE: if changing cvars, do it within this block. This allows them // to be overridden when testing driver fixes, etc. but only sets // them to their default state when the hardware is first installed/run. // extern qboolean Sys_LowPhysicalMemory(); if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) ) { #if MAC_JK2 if ( Sys_LowPhysicalMemory() ) { Cvar_Set( "s_khz", "11" ); // this will get called before S_Init Cvar_Set( "cg_VariantSoundCap", "2" ); Cvar_Set( "s_allowDynamicMusic","0" ); } #endif //reset to defaults Cvar_Set( "r_picmip", "1" ); if ( strstr( buf, "matrox" ) ) { Cvar_Set( "r_allowExtensions", "0" ); } else // Savage3D and Savage4 should always have trilinear enabled if ( strstr( buf, "savage3d" ) || strstr( buf, "s3 savage4" ) || strstr( buf, "geforce" ) ) { Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); } else { Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" ); } if ( strstr( buf, "kyro" ) ) { Cvar_Set( "r_ext_texture_filter_anisotropic", "0" ); //KYROs have it avail, but suck at it! Cvar_Set( "r_ext_preferred_tc_method", "1" ); //(Use DXT1 instead of DXT5 - same quality but much better performance on KYRO) } GLimp_Extensions(); CheckErrors( __FILE__, __LINE__ ); #if MAC_JK2 || MAC_JKJA //this must be a really sucky card! if ( ( glConfig.textureCompression == TC_NONE ) || ( glConfig.maxActiveTextures < 2 ) || ( glConfig.maxTextureSize <= 512 ) ) { Cvar_Set( "r_ext_texture_filter_anisotropic", "0" ); Cvar_Set( "r_picmip", "2" ); Cvar_Set( "r_colorbits", "16" ); Cvar_Set( "r_texturebits", "16" ); Cvar_Set( "r_mode", "3" ); //force 640 Cmd_ExecuteString( "exec low.cfg\n" ); //get the rest which can be pulled in after init } #endif } Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string ); GLimp_Extensions(); CheckErrors( __FILE__, __LINE__ ); GLimp_SetFSAA(); // draw something to show that GL is alive glClearColor( 1, 0.5, 0.2, 0 ); glClear( GL_COLOR_BUFFER_BIT ); GLimp_EndFrame(); GLimp_AglDescribe_f(); } /* =============== GLimp_EndFrame =============== */ Q3DEF void GLimp_EndFrame( void ) { // check for variable changes if ( r_swapInterval->modified ) { r_swapInterval->modified = qfalse; VID_Printf( PRINT_ALL, "Changing AGL_SWAP_INTERVAL\n" ); aglSetInteger( sys_gl.context, AGL_SWAP_INTERVAL, (long *)&r_swapInterval->integer ); } if ( r_ati_fsaa_samples->modified ) { //DAJ added changed for ati fsaa r_ati_fsaa_samples->modified = qfalse; if ( IsRadeon() /*&& gSystemVersion >= 0x0922*/ ) { if ( r_ati_fsaa_samples->integer == 1 || r_ati_fsaa_samples->integer == 2 || r_ati_fsaa_samples->integer == 4 ) { GLenum err; VID_Printf( PRINT_ALL, "Changing ATI FSAA samples to %d\n", r_ati_fsaa_samples->integer ); aglSetInteger( sys_gl.context, ATI_FSAA_SAMPLES, (const long*)&r_ati_fsaa_samples->integer ); // Flush AGL_BAD_ENUM, which can occur if we're using older OS/drivers err = aglGetError(); } else { VID_Printf( PRINT_ALL, "Valid ATI FSAA samples are 1, 2, 4. Resetting to 1\n" ); Cvar_Set( "r_ati_fsaa_samples", "1" ); } } } if ( r_nv_quincunx->modified ) { r_nv_quincunx->modified = qfalse; GLimp_SetFSAA(); } // make sure the event loop is pumped Sys_SendKeyEvents(); _aglSwapBuffers(); #if MAC_TIME_LIMIT CheckTimeLimit(); #endif } /* =============== GLimp_Shutdown =============== */ void GLimp_Shutdown( void ) { if ( sys_gl.systemGammas ) { { CGGammaTable *cgGamma = (CGGammaTable *) sys_gl.systemGammas; CGSetDisplayTransferByTable( NULL, 256, cgGamma->red, cgGamma->green, cgGamma->blue ); free( sys_gl.systemGammas ); } sys_gl.systemGammas = 0; } _aglDisposeGameContext(); memset( &glConfig, 0, sizeof( glConfig ) ); if ( gDrawSprocket ) { delete gDrawSprocket; } } #if MAC_WOLF Q3DEF void GLimp_LogComment( char *comment ) #else Q3DEF void GLimp_LogComment( const char *comment ) #endif { if ( sys_gl.log_fp ) { fprintf( sys_gl.log_fp, "%s", comment ); } } /* =============== GLimp_SetGamma =============== */ void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] ) { int i; { // Convert values from range 0-255 to 0.0-1.0 for ( i = 0 ; i < 256 ; i++ ) { sys_gl.gameGamma.red[i] = (float)red[i] / 256.0; sys_gl.gameGamma.green[i] = (float)green[i] / 256.0; sys_gl.gameGamma.blue[i] = (float)blue[i] / 256.0; } sHasGameGamma = true; { CGDisplayErr cgErr; cgErr = CGSetDisplayTransferByTable( NULL, 256, sys_gl.gameGamma.red, sys_gl.gameGamma.green, sys_gl.gameGamma.blue ); #if _DEBUG VID_Printf( PRINT_ALL, "CoreGraphics gamma table set, err: %d\n", cgErr ); #endif } } } qboolean GLimp_ChangeMode( int mode ) { int colorDepth, zDepth; int fsaaSamples; long value; // get mode info mode = r_mode->integer; VID_Printf( PRINT_ALL, "...setting mode %d:", mode ); #if MAC_JKJA if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, mode ) ) #else if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) ) #endif { VID_Printf( PRINT_ALL, " invalid mode\n" ); return qfalse; } VID_Printf( PRINT_ALL, " %dx%d\n", glConfig.vidWidth, glConfig.vidHeight ); #if MAC_DEBUG VID_Printf( PRINT_ALL, "...colorbits: %d\n", r_colorbits->integer ); VID_Printf( PRINT_ALL, "...depthbits: %d\n", r_depthbits->integer ); VID_Printf( PRINT_ALL, "...stencilbits: %d\n", r_stencilbits->integer ); #endif colorDepth = r_colorbits->integer; if ( colorDepth == 0 ) { colorDepth = 16; } zDepth = r_depthbits->integer; if ( zDepth == 0 ) { zDepth = 24; } switch ( r_nv_fsaa_samples->integer ) { case 2: fsaaSamples = 2; break; case 4: fsaaSamples = 4; break; case 0: case 1: default: fsaaSamples = 0; break; } if ( !_aglSetGameContext( glConfig.vidWidth, glConfig.vidHeight, colorDepth, zDepth, r_stencilbits->integer, gRefreshRate, sys_gl.displayID, !r_fullscreen->integer, fsaaSamples ) ) { // return qfalse; Com_Error( ERR_FATAL, "Couldn't start OpenGL!" ); } sys_gl.context = aglGetCurrentContext(); sys_gl.device = gDrawSprocket->GetGDevice(); sys_gl.fmt = gPixelFormat; // glConfig updates // glConfig.displayFrequency = glInfo.freq; // glConfig.isFullscreen = glInfo.fFullscreen; // glConfig.vidHeight = glInfo.height; // glConfig.vidWidth = glInfo.width; aglDescribePixelFormat( sys_gl.fmt, AGL_RED_SIZE, &value ); glConfig.colorBits = value; aglDescribePixelFormat( sys_gl.fmt, AGL_GREEN_SIZE, &value ); glConfig.colorBits += value; aglDescribePixelFormat( sys_gl.fmt, AGL_BLUE_SIZE, &value ); glConfig.colorBits += value; aglDescribePixelFormat( sys_gl.fmt, AGL_STENCIL_SIZE, &value ); glConfig.stencilBits = value; aglDescribePixelFormat( sys_gl.fmt, AGL_DEPTH_SIZE, &value ); glConfig.depthBits = value; CheckErrors( __FILE__, __LINE__ ); glConfig.isFullscreen = (qboolean) r_fullscreen->integer; return qtrue; } Q3DEF qboolean GLimp_ChangeFullscreen( qboolean fullscreen ) { return qfalse; } #pragma mark - #pragma mark ¥ SMP Calls #if PTHREADS #pragma mark * pthreads sem_t renderCommandsEvent; sem_t renderCompletedEvent; sem_t renderActiveEvent; void ( *glimpRenderThread )( void ); void *GLimp_RenderThreadWrapper( void *stub ) { glimpRenderThread(); return NULL; } /* ======================= GLimp_SpawnRenderThread ======================= */ pthread_t renderThreadHandle; Q3DEF qboolean GLimp_SpawnRenderThread( void ( *function )( void ) ) { sem_init( &renderCommandsEvent, 0, 0 ); sem_init( &renderCompletedEvent, 0, 0 ); sem_init( &renderActiveEvent, 0, 0 ); glimpRenderThread = function; if ( pthread_create( &renderThreadHandle, NULL, GLimp_RenderThreadWrapper, NULL ) ) { return qfalse; } return qtrue; } static void *smpData; static int glXErrors; Q3DEF void *GLimp_RendererSleep( void ) { void *data; // after this, the front end can exit GLimp_FrontEndSleep sem_post( &renderCompletedEvent ); sem_wait( &renderCommandsEvent ); data = smpData; // after this, the main thread can exit GLimp_WakeRenderer sem_post( &renderActiveEvent ); return data; } Q3DEF void GLimp_FrontEndSleep( void ) { sem_wait( &renderCompletedEvent ); } Q3DEF void GLimp_WakeRenderer( void *data ) { smpData = data; // after this, the renderer can continue through GLimp_RendererSleep sem_post( &renderCommandsEvent ); sem_wait( &renderActiveEvent ); } #elif MPLIB #pragma mark * MP library MPSemaphoreID renderCommandsEvent; MPSemaphoreID renderCompletedEvent; MPSemaphoreID renderActiveEvent; MPSemaphoreID g2_frontend; MPSemaphoreID g2_backend; MPCriticalRegionID CritID = NULL; extern AGLContext gAGLContext_Primary; extern AGLContext gAGLContext_Secondary; void ( *glimpRenderThread )( void ); OSStatus GLimp_RenderThreadWrapper( void *stub ) { GLboolean ok; // Disable the primary and set up the secondary context for rendering _aglUseSecondaryContext(); GL_SetDefaultState(); GLimp_SetFSAA(); // draw something to show that the second context is alive glClearColor( 0.0, 1.0, 0.0, 0 ); glClear( GL_COLOR_BUFFER_BIT ); GLimp_EndFrame(); // get the extensions // GLimp_Extensions(); // CheckErrors(); // GLimp_AglDescribe_f(); glimpRenderThread(); return noErr; } /* ======================= GLimp_SpawnRenderThread ======================= */ MPTaskID renderThreadTask; Q3DEF qboolean GLimp_SpawnRenderThread( void ( *function )( void ) ) { OSStatus status = noErr; ItemCount numCPU = 0; if ( gSystemVersion < 0x1010 ) { VID_Printf( PRINT_ALL, "SMP support requires MacOS 10.1 or higher.\n" ); return qfalse; } if ( !MPLibraryIsLoaded() ) { return qfalse; } numCPU = MPProcessors(); VID_Printf( PRINT_ALL, "Number of CPUs: %d\n", numCPU ); // Disable SMP support if only 1 CPU if ( numCPU < 2 ) { return qfalse; } #if MAC_JK2 VID_Printf( PRINT_ALL, "SMP is not supported in this game.\n" ); return qfalse; #endif // We don't use MPCreateBinary Semaphore because it sets the inital value // of the semaphore to 1 (i.e. fired), which is not what we want. MPCreateSemaphore( 1, 0, &renderCommandsEvent ); MPCreateSemaphore( 1, 0, &renderCompletedEvent ); MPCreateSemaphore( 1, 0, &renderActiveEvent ); MPCreateSemaphore( 1, 0, &g2_backend ); MPCreateSemaphore( 1, 0, &g2_frontend ); MPCreateCriticalRegion( &CritID ); glimpRenderThread = function; // Flush the primary context - we're about to get rid of it glFinish(); GLimp_EndFrame(); status = MPCreateTask( GLimp_RenderThreadWrapper,(void *) NULL,0,NULL,0,0,kNilOptions, &renderThreadTask ); if ( status != noErr ) { VID_Printf( PRINT_ALL, "Can't create MP task, err #%d\n", status ); return qfalse; } return qtrue; } static void *smpData; static int glXErrors; static Boolean asleep = false; Q3DEF void *GLimp_RendererSleep( void ) { void *data; // after this, the front end can exit GLimp_FrontEndSleep MPSignalSemaphore( renderCompletedEvent ); // Loop indefinitely until we are asked to wake up via a renderCommandEvent MPWaitOnSemaphore( renderCommandsEvent, kDurationForever ); data = smpData; // after this, the main thread can exit GLimp_WakeRenderer MPSignalSemaphore( renderActiveEvent ); return data; } Q3DEF void GLimp_FrontEndSleep( void ) { // If we're already asleep, then we don't need to wait // The q3 core can call this routine 2 or more times without a subsequent call // to WakeRenderer, which leaves the semaphores in a deadlock. MPEnterCriticalRegion( CritID, kDurationForever ); //DAJ if ( asleep ) { MPExitCriticalRegion( CritID ); return; } MPWaitOnSemaphore( renderCompletedEvent, kDurationForever ); asleep = true; MPExitCriticalRegion( CritID ); // We walk a fine line here. The Q3 core will issue OpenGL commands // in the main thread once it has determined that the back-end renderer // has completed. It makes this determination by having this routine // return. Because of this, we're going to point the main thread context // to the AGL context that is set up for the second thread right now // and hope for the best. aglSetCurrentContext( gAGLContext_Secondary ); } Q3DEF void GLimp_WakeRenderer( void *data ) { smpData = data; // We're getting ready to return control to the renderer thread, so // we're going to switch the main thread context back to the primary // while the backend is running. We can't have the same AGL context // active in two different threads at once, or we risk confusing the // OpenGL command pipeline for that context and a potential kernel panic. aglSetCurrentContext( gAGLContext_Primary ); // after this, the renderer can continue through GLimp_RendererSleep MPSignalSemaphore( renderCommandsEvent ); MPWaitOnSemaphore( renderActiveEvent, kDurationForever ); MPEnterCriticalRegion( CritID, kDurationForever ); //DAJ asleep = false; MPExitCriticalRegion( CritID ); } #else #pragma mark * no threading Q3DEF qboolean GLimp_SpawnRenderThread( void ( *function )( void ) ) { return qfalse; } Q3DEF void *GLimp_RendererSleep( void ) { return NULL; } Q3DEF void GLimp_FrontEndSleep( void ) { } Q3DEF void GLimp_WakeRenderer( void * data ) { } #endif