summaryrefslogtreecommitdiff
path: root/src/sdl/sdl_glimp.c
diff options
context:
space:
mode:
authorTim Angus <tim@ngus.net>2007-09-05 22:05:32 +0000
committerTim Angus <tim@ngus.net>2007-09-05 22:05:32 +0000
commitc99975c73ae2941751d86d3b8466454c5ad122c0 (patch)
treeb5bb65655fbdbc22201426046092939bcf696abe /src/sdl/sdl_glimp.c
parent27ad2c0e19891f7f2454c9eedf76878dc1942ba6 (diff)
* Merge of ioq3-r1163
+ SDL everywhere + New R_LoadImage + net_ip.c + Window icon + GUI based dedicated server support + Compile time linked OpenGL * Remove ui_menuFiles cvar
Diffstat (limited to 'src/sdl/sdl_glimp.c')
-rw-r--r--src/sdl/sdl_glimp.c804
1 files changed, 804 insertions, 0 deletions
diff --git a/src/sdl/sdl_glimp.c b/src/sdl/sdl_glimp.c
new file mode 100644
index 00000000..2344e5c9
--- /dev/null
+++ b/src/sdl/sdl_glimp.c
@@ -0,0 +1,804 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena 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 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#include "SDL.h"
+
+#ifdef SMP
+#include "SDL_thread.h"
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../renderer/tr_local.h"
+#include "../client/client.h"
+#include "../sys/sys_local.h"
+#include "sdl_icon.h"
+
+/* Just hack it for now. */
+#ifdef MACOS_X
+typedef CGLContextObj QGLContext;
+#define GLimp_GetCurrentContext() CGLGetCurrentContext()
+#define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx)
+#else
+typedef void *QGLContext;
+#define GLimp_GetCurrentContext() (NULL)
+#define GLimp_SetCurrentContext(ctx)
+#endif
+
+static QGLContext opengl_context;
+
+typedef enum
+{
+ RSERR_OK,
+
+ RSERR_INVALID_FULLSCREEN,
+ RSERR_INVALID_MODE,
+
+ RSERR_UNKNOWN
+} rserr_t;
+
+static SDL_Surface *screen = NULL;
+
+cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obtained
+
+PFNGLMULTITEXCOORD2FARBPROC qglMultiTexCoord2fARB;
+PFNGLACTIVETEXTUREARBPROC qglActiveTextureARB;
+PFNGLCLIENTACTIVETEXTUREARBPROC qglClientActiveTextureARB;
+
+PFNGLLOCKARRAYSEXTPROC qglLockArraysEXT;
+PFNGLUNLOCKARRAYSEXTPROC qglUnlockArraysEXT;
+
+/*
+===============
+GLimp_Shutdown
+===============
+*/
+void GLimp_Shutdown( void )
+{
+ IN_Shutdown();
+
+ SDL_QuitSubSystem( SDL_INIT_VIDEO );
+ screen = NULL;
+
+ memset( &glConfig, 0, sizeof( glConfig ) );
+ memset( &glState, 0, sizeof( glState ) );
+}
+
+/*
+===============
+GLimp_LogComment
+===============
+*/
+void GLimp_LogComment( char *comment )
+{
+}
+
+/*
+===============
+GLimp_SetMode
+===============
+*/
+static int GLimp_SetMode( int mode, qboolean fullscreen )
+{
+ const char* glstring;
+ int sdlcolorbits;
+ int colorbits, depthbits, stencilbits;
+ int tcolorbits, tdepthbits, tstencilbits;
+ int i = 0;
+ SDL_Surface *vidscreen = NULL;
+
+ ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
+
+ ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
+
+ if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
+ {
+ ri.Printf( PRINT_ALL, " invalid mode\n" );
+ return RSERR_INVALID_MODE;
+ }
+ ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
+
+ Uint32 flags = SDL_OPENGL;
+ if (fullscreen)
+ {
+ flags |= SDL_FULLSCREEN;
+ glConfig.isFullscreen = qtrue;
+ }
+ else
+ glConfig.isFullscreen = qfalse;
+
+ if (!r_colorbits->value)
+ colorbits = 24;
+ else
+ colorbits = r_colorbits->value;
+
+ if (!r_depthbits->value)
+ depthbits = 24;
+ else
+ depthbits = r_depthbits->value;
+ stencilbits = r_stencilbits->value;
+
+ for (i = 0; i < 16; i++)
+ {
+ // 0 - default
+ // 1 - minus colorbits
+ // 2 - minus depthbits
+ // 3 - minus stencil
+ if ((i % 4) == 0 && i)
+ {
+ // one pass, reduce
+ switch (i / 4)
+ {
+ case 2 :
+ if (colorbits == 24)
+ colorbits = 16;
+ break;
+ case 1 :
+ if (depthbits == 24)
+ depthbits = 16;
+ else if (depthbits == 16)
+ depthbits = 8;
+ case 3 :
+ if (stencilbits == 24)
+ stencilbits = 16;
+ else if (stencilbits == 16)
+ stencilbits = 8;
+ }
+ }
+
+ tcolorbits = colorbits;
+ tdepthbits = depthbits;
+ tstencilbits = stencilbits;
+
+ if ((i % 4) == 3)
+ { // reduce colorbits
+ if (tcolorbits == 24)
+ tcolorbits = 16;
+ }
+
+ if ((i % 4) == 2)
+ { // reduce depthbits
+ if (tdepthbits == 24)
+ tdepthbits = 16;
+ else if (tdepthbits == 16)
+ tdepthbits = 8;
+ }
+
+ if ((i % 4) == 1)
+ { // reduce stencilbits
+ if (tstencilbits == 24)
+ tstencilbits = 16;
+ else if (tstencilbits == 16)
+ tstencilbits = 8;
+ else
+ tstencilbits = 0;
+ }
+
+ sdlcolorbits = 4;
+ if (tcolorbits == 24)
+ sdlcolorbits = 8;
+
+ SDL_GL_SetAttribute( SDL_GL_RED_SIZE, sdlcolorbits );
+ SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, sdlcolorbits );
+ SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, sdlcolorbits );
+ SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, tdepthbits );
+ SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, tstencilbits );
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+
+ // If not allowing software GL, demand accelerated
+ if( !r_allowSoftwareGL->integer )
+ {
+ if( SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ) < 0 )
+ {
+ ri.Printf( PRINT_ALL, "Unable to guarantee accelerated "
+ "visual with libSDL < 1.2.10\n" );
+ }
+ }
+
+ if( SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, r_swapInterval->integer ) < 0 )
+ ri.Printf( PRINT_ALL, "r_swapInterval requires libSDL >= 1.2.10\n" );
+
+#ifdef USE_ICON
+ {
+ SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
+ (void *)CLIENT_WINDOW_ICON.pixel_data,
+ CLIENT_WINDOW_ICON.width,
+ CLIENT_WINDOW_ICON.height,
+ CLIENT_WINDOW_ICON.bytes_per_pixel * 8,
+ CLIENT_WINDOW_ICON.bytes_per_pixel * CLIENT_WINDOW_ICON.width,
+#ifdef Q3_LITTLE_ENDIAN
+ 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
+#else
+ 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
+#endif
+ );
+
+ SDL_WM_SetIcon( icon, NULL );
+ SDL_FreeSurface( icon );
+ }
+#endif
+
+ SDL_WM_SetCaption(CLIENT_WINDOW_TITLE, CLIENT_WINDOW_MIN_TITLE);
+ SDL_ShowCursor(0);
+
+ if (!(vidscreen = SDL_SetVideoMode(glConfig.vidWidth, glConfig.vidHeight, colorbits, flags)))
+ {
+ ri.Printf( PRINT_DEVELOPER, "SDL_SetVideoMode failed: %s\n", SDL_GetError( ) );
+ continue;
+ }
+
+ opengl_context = GLimp_GetCurrentContext();
+
+ ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
+ sdlcolorbits, sdlcolorbits, sdlcolorbits, tdepthbits, tstencilbits);
+
+ glConfig.colorBits = tcolorbits;
+ glConfig.depthBits = tdepthbits;
+ glConfig.stencilBits = tstencilbits;
+ break;
+ }
+
+ if (!vidscreen)
+ {
+ ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
+ return RSERR_INVALID_MODE;
+ }
+
+ screen = vidscreen;
+
+ glstring = (char *) qglGetString (GL_RENDERER);
+ ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
+
+ return RSERR_OK;
+}
+
+/*
+===============
+GLimp_StartDriverAndSetMode
+===============
+*/
+static qboolean GLimp_StartDriverAndSetMode( int mode, qboolean fullscreen )
+{
+ rserr_t err;
+
+ if (!SDL_WasInit(SDL_INIT_VIDEO))
+ {
+ ri.Printf( PRINT_ALL, "SDL_Init( SDL_INIT_VIDEO )... ");
+ if (SDL_Init(SDL_INIT_VIDEO) == -1)
+ {
+ ri.Printf( PRINT_ALL, "FAILED (%s)\n", SDL_GetError());
+ return qfalse;
+ }
+ ri.Printf( PRINT_ALL, "OK\n");
+ }
+
+ if (fullscreen && Cvar_VariableIntegerValue( "in_nograb" ) )
+ {
+ ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
+ ri.Cvar_Set( "r_fullscreen", "0" );
+ r_fullscreen->modified = qfalse;
+ fullscreen = qfalse;
+ }
+
+ err = GLimp_SetMode( mode, fullscreen );
+
+ switch ( err )
+ {
+ case RSERR_INVALID_FULLSCREEN:
+ ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
+ return qfalse;
+ case RSERR_INVALID_MODE:
+ ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
+ return qfalse;
+ default:
+ break;
+ }
+
+ return qtrue;
+}
+/*
+===============
+GLimp_InitExtensions
+===============
+*/
+static void GLimp_InitExtensions( void )
+{
+ if ( !r_allowExtensions->integer )
+ {
+ ri.Printf( PRINT_ALL, "* IGNORING OPENGL EXTENSIONS *\n" );
+ return;
+ }
+
+ ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
+
+ // GL_S3_s3tc
+ if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
+ {
+ if ( r_ext_compressed_textures->value )
+ {
+ glConfig.textureCompression = TC_S3TC;
+ ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
+ }
+ else
+ {
+ glConfig.textureCompression = TC_NONE;
+ ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
+ }
+ }
+ else
+ {
+ glConfig.textureCompression = TC_NONE;
+ ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
+ }
+
+ // GL_EXT_texture_env_add
+ glConfig.textureEnvAddAvailable = qfalse;
+ if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
+ {
+ if ( r_ext_texture_env_add->integer )
+ {
+ glConfig.textureEnvAddAvailable = qtrue;
+ ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
+ }
+ else
+ {
+ glConfig.textureEnvAddAvailable = qfalse;
+ ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
+ }
+
+ // GL_ARB_multitexture
+ qglMultiTexCoord2fARB = NULL;
+ qglActiveTextureARB = NULL;
+ qglClientActiveTextureARB = NULL;
+ if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
+ {
+ if ( r_ext_multitexture->value )
+ {
+ qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) SDL_GL_GetProcAddress( "glMultiTexCoord2fARB" );
+ qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) SDL_GL_GetProcAddress( "glActiveTextureARB" );
+ qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) SDL_GL_GetProcAddress( "glClientActiveTextureARB" );
+
+ if ( qglActiveTextureARB )
+ {
+ GLint glint = 0;
+ qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glint );
+ glConfig.maxActiveTextures = (int) glint;
+ if ( glConfig.maxActiveTextures > 1 )
+ {
+ ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
+ }
+ else
+ {
+ qglMultiTexCoord2fARB = NULL;
+ qglActiveTextureARB = NULL;
+ qglClientActiveTextureARB = NULL;
+ ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
+ }
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
+ }
+
+ // GL_EXT_compiled_vertex_array
+ if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
+ {
+ if ( r_ext_compiled_vertex_array->value )
+ {
+ ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
+ qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) SDL_GL_GetProcAddress( "glLockArraysEXT" );
+ qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) SDL_GL_GetProcAddress( "glUnlockArraysEXT" );
+ if (!qglLockArraysEXT || !qglUnlockArraysEXT)
+ {
+ ri.Error (ERR_FATAL, "bad getprocaddress");
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
+ }
+
+ glConfig.textureFilterAnisotropic = qfalse;
+ if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) )
+ {
+ if ( r_ext_texture_filter_anisotropic->integer ) {
+ qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&glConfig.maxAnisotropy );
+ if ( glConfig.maxAnisotropy <= 0 ) {
+ ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" );
+ glConfig.maxAnisotropy = 0;
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", glConfig.maxAnisotropy );
+ glConfig.textureFilterAnisotropic = qtrue;
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" );
+ }
+ }
+ else
+ {
+ ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" );
+ }
+}
+
+#define R_MODE_FALLBACK 3 // 640 * 480
+
+/*
+===============
+GLimp_Init
+
+This routine is responsible for initializing the OS specific portions
+of OpenGL
+===============
+*/
+void GLimp_Init( void )
+{
+ qboolean success = qtrue;
+
+ r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
+
+ // create the window and set up the context
+ if( !GLimp_StartDriverAndSetMode( r_mode->integer, r_fullscreen->integer ) )
+ {
+ if( r_mode->integer != R_MODE_FALLBACK )
+ {
+ ri.Printf( PRINT_ALL, "Setting r_mode %d failed, falling back on r_mode %d",
+ r_mode->integer, R_MODE_FALLBACK );
+ if( !GLimp_StartDriverAndSetMode( R_MODE_FALLBACK, r_fullscreen->integer ) )
+ success = qfalse;
+ }
+ else
+ success = qfalse;
+ }
+
+ if( !success )
+ ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
+
+ // This values force the UI to disable driver selection
+ glConfig.driverType = GLDRV_ICD;
+ glConfig.hardwareType = GLHW_GENERIC;
+ glConfig.deviceSupportsGamma = !!( SDL_SetGamma( 1.0f, 1.0f, 1.0f ) >= 0 );
+
+ // get our config strings
+ Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
+ Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
+ if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
+ glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
+ Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
+ Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
+
+ // initialize extensions
+ GLimp_InitExtensions( );
+
+ // This depends on SDL_INIT_VIDEO, hence having it here
+ IN_Init( );
+
+ return;
+}
+
+
+/*
+===============
+GLimp_EndFrame
+
+Responsible for doing a swapbuffers
+===============
+*/
+void GLimp_EndFrame( void )
+{
+ // don't flip if drawing to front buffer
+ if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
+ {
+ SDL_GL_SwapBuffers();
+ }
+
+ if( r_fullscreen->modified )
+ {
+ qboolean fullscreen;
+ qboolean sdlToggled = qfalse;
+ SDL_Surface *s = SDL_GetVideoSurface( );
+
+ if( s )
+ {
+ // Find out the current state
+ if( s->flags & SDL_FULLSCREEN )
+ fullscreen = qtrue;
+ else
+ fullscreen = qfalse;
+
+ // Is the state we want different from the current state?
+ if( !!r_fullscreen->integer != fullscreen )
+ sdlToggled = SDL_WM_ToggleFullScreen( s );
+ else
+ sdlToggled = qtrue;
+ }
+
+ // SDL_WM_ToggleFullScreen didn't work, so do it the slow way
+ if( !sdlToggled )
+ Cbuf_AddText( "vid_restart" );
+
+ r_fullscreen->modified = qfalse;
+ }
+}
+
+
+
+#ifdef SMP
+/*
+===========================================================
+
+SMP acceleration
+
+===========================================================
+*/
+
+/*
+ * I have no idea if this will even work...most platforms don't offer
+ * thread-safe OpenGL libraries, and it looks like the original Linux
+ * code counted on each thread claiming the GL context with glXMakeCurrent(),
+ * which you can't currently do in SDL. We'll just have to hope for the best.
+ */
+
+static SDL_mutex *smpMutex = NULL;
+static SDL_cond *renderCommandsEvent = NULL;
+static SDL_cond *renderCompletedEvent = NULL;
+static void (*glimpRenderThread)( void ) = NULL;
+static SDL_Thread *renderThread = NULL;
+
+/*
+===============
+GLimp_ShutdownRenderThread
+===============
+*/
+static void GLimp_ShutdownRenderThread(void)
+{
+ if (smpMutex != NULL)
+ {
+ SDL_DestroyMutex(smpMutex);
+ smpMutex = NULL;
+ }
+
+ if (renderCommandsEvent != NULL)
+ {
+ SDL_DestroyCond(renderCommandsEvent);
+ renderCommandsEvent = NULL;
+ }
+
+ if (renderCompletedEvent != NULL)
+ {
+ SDL_DestroyCond(renderCompletedEvent);
+ renderCompletedEvent = NULL;
+ }
+
+ glimpRenderThread = NULL;
+}
+
+/*
+===============
+GLimp_RenderThreadWrapper
+===============
+*/
+static int GLimp_RenderThreadWrapper( void *arg )
+{
+ Com_Printf( "Render thread starting\n" );
+
+ glimpRenderThread();
+
+ GLimp_SetCurrentContext(NULL);
+
+ Com_Printf( "Render thread terminating\n" );
+
+ return 0;
+}
+
+/*
+===============
+GLimp_SpawnRenderThread
+===============
+*/
+qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
+{
+ static qboolean warned = qfalse;
+ if (!warned)
+ {
+ Com_Printf("WARNING: You enable r_smp at your own risk!\n");
+ warned = qtrue;
+ }
+
+#ifndef MACOS_X
+ return qfalse; /* better safe than sorry for now. */
+#endif
+
+ if (renderThread != NULL) /* hopefully just a zombie at this point... */
+ {
+ Com_Printf("Already a render thread? Trying to clean it up...\n");
+ SDL_WaitThread(renderThread, NULL);
+ renderThread = NULL;
+ GLimp_ShutdownRenderThread();
+ }
+
+ smpMutex = SDL_CreateMutex();
+ if (smpMutex == NULL)
+ {
+ Com_Printf( "smpMutex creation failed: %s\n", SDL_GetError() );
+ GLimp_ShutdownRenderThread();
+ return qfalse;
+ }
+
+ renderCommandsEvent = SDL_CreateCond();
+ if (renderCommandsEvent == NULL)
+ {
+ Com_Printf( "renderCommandsEvent creation failed: %s\n", SDL_GetError() );
+ GLimp_ShutdownRenderThread();
+ return qfalse;
+ }
+
+ renderCompletedEvent = SDL_CreateCond();
+ if (renderCompletedEvent == NULL)
+ {
+ Com_Printf( "renderCompletedEvent creation failed: %s\n", SDL_GetError() );
+ GLimp_ShutdownRenderThread();
+ return qfalse;
+ }
+
+ glimpRenderThread = function;
+ renderThread = SDL_CreateThread(GLimp_RenderThreadWrapper, NULL);
+ if ( renderThread == NULL )
+ {
+ ri.Printf( PRINT_ALL, "SDL_CreateThread() returned %s", SDL_GetError() );
+ GLimp_ShutdownRenderThread();
+ return qfalse;
+ }
+ else
+ {
+ // tma 01/09/07: don't think this is necessary anyway?
+ //
+ // !!! FIXME: No detach API available in SDL!
+ //ret = pthread_detach( renderThread );
+ //if ( ret ) {
+ //ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
+ //}
+ }
+
+ return qtrue;
+}
+
+static volatile void *smpData = NULL;
+static volatile qboolean smpDataReady;
+
+/*
+===============
+GLimp_RendererSleep
+===============
+*/
+void *GLimp_RendererSleep( void )
+{
+ void *data = NULL;
+
+ GLimp_SetCurrentContext(NULL);
+
+ SDL_LockMutex(smpMutex);
+ {
+ smpData = NULL;
+ smpDataReady = qfalse;
+
+ // after this, the front end can exit GLimp_FrontEndSleep
+ SDL_CondSignal(renderCompletedEvent);
+
+ while ( !smpDataReady )
+ SDL_CondWait(renderCommandsEvent, smpMutex);
+
+ data = (void *)smpData;
+ }
+ SDL_UnlockMutex(smpMutex);
+
+ GLimp_SetCurrentContext(opengl_context);
+
+ return data;
+}
+
+/*
+===============
+GLimp_FrontEndSleep
+===============
+*/
+void GLimp_FrontEndSleep( void )
+{
+ SDL_LockMutex(smpMutex);
+ {
+ while ( smpData )
+ SDL_CondWait(renderCompletedEvent, smpMutex);
+ }
+ SDL_UnlockMutex(smpMutex);
+
+ GLimp_SetCurrentContext(opengl_context);
+}
+
+/*
+===============
+GLimp_WakeRenderer
+===============
+*/
+void GLimp_WakeRenderer( void *data )
+{
+ GLimp_SetCurrentContext(NULL);
+
+ SDL_LockMutex(smpMutex);
+ {
+ assert( smpData == NULL );
+ smpData = data;
+ smpDataReady = qtrue;
+
+ // after this, the renderer can continue through GLimp_RendererSleep
+ SDL_CondSignal(renderCommandsEvent);
+ }
+ SDL_UnlockMutex(smpMutex);
+}
+
+#else
+
+// No SMP - stubs
+void GLimp_RenderThreadWrapper( void *arg )
+{
+}
+
+qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
+{
+ ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
+ return qfalse;
+}
+
+void *GLimp_RendererSleep( void )
+{
+ return NULL;
+}
+
+void GLimp_FrontEndSleep( void )
+{
+}
+
+void GLimp_WakeRenderer( void *data )
+{
+}
+
+#endif