2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
23
#ifdef USE_LOCAL_HEADERS
29
#if !SDL_VERSION_ATLEAST(1, 2, 10)
30
#define SDL_GL_ACCELERATED_VISUAL 15
31
#define SDL_GL_SWAP_CONTROL 16
32
#elif MINSDL_PATCH >= 10
33
#error Code block no longer necessary, please remove
37
# ifdef USE_LOCAL_HEADERS
38
# include "SDL_thread.h"
40
# include <SDL_thread.h>
49
#include "../renderer/tr_local.h"
50
#include "../client/client.h"
51
#include "../sys/sys_local.h"
54
/* Just hack it for now. */
56
#include <OpenGL/OpenGL.h>
57
typedef CGLContextObj QGLContext;
58
#define GLimp_GetCurrentContext() CGLGetCurrentContext()
59
#define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx)
61
typedef void *QGLContext;
62
#define GLimp_GetCurrentContext() (NULL)
63
#define GLimp_SetCurrentContext(ctx)
66
static QGLContext opengl_context;
72
RSERR_INVALID_FULLSCREEN,
78
static SDL_Surface *screen = NULL;
80
cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obtained
82
void (APIENTRYP qglActiveTextureARB) (GLenum texture);
83
void (APIENTRYP qglClientActiveTextureARB) (GLenum texture);
84
void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t);
86
void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count);
87
void (APIENTRYP qglUnlockArraysEXT) (void);
94
void GLimp_Shutdown( void )
98
SDL_QuitSubSystem( SDL_INIT_VIDEO );
101
Com_Memset( &glConfig, 0, sizeof( glConfig ) );
102
Com_Memset( &glState, 0, sizeof( glState ) );
110
void GLimp_LogComment( char *comment )
119
static int GLimp_CompareModes( const void *a, const void *b )
121
const float ASPECT_EPSILON = 0.001f;
122
SDL_Rect *modeA = *(SDL_Rect **)a;
123
SDL_Rect *modeB = *(SDL_Rect **)b;
124
float aspectDiffA = fabs( ( (float)modeA->w / (float)modeA->h ) - displayAspect );
125
float aspectDiffB = fabs( ( (float)modeB->w / (float)modeB->h ) - displayAspect );
126
float aspectDiffsDiff = aspectDiffA - aspectDiffB;
128
if( aspectDiffsDiff > ASPECT_EPSILON )
130
else if( aspectDiffsDiff < -ASPECT_EPSILON )
134
if( modeA->w == modeB->w )
135
return modeA->h - modeB->h;
137
return modeA->w - modeB->w;
143
GLimp_DetectAvailableModes
146
static void GLimp_DetectAvailableModes(void)
148
char buf[ MAX_STRING_CHARS ] = { 0 };
153
modes = SDL_ListModes( NULL, SDL_OPENGL | SDL_FULLSCREEN );
157
ri.Printf( PRINT_WARNING, "Can't get list of available modes\n" );
161
if( modes == (SDL_Rect **)-1 )
163
ri.Printf( PRINT_ALL, "Display supports any resolution\n" );
164
return; // can set any resolution
167
for( numModes = 0; modes[ numModes ]; numModes++ );
170
qsort( modes+1, numModes-1, sizeof( SDL_Rect* ), GLimp_CompareModes );
172
for( i = 0; i < numModes; i++ )
174
const char *newModeString = va( "%ux%u ", modes[ i ]->w, modes[ i ]->h );
176
if( strlen( newModeString ) < (int)sizeof( buf ) - strlen( buf ) )
177
Q_strcat( buf, sizeof( buf ), newModeString );
179
ri.Printf( PRINT_WARNING, "Skipping mode %ux%x, buffer too small\n", modes[i]->w, modes[i]->h );
184
buf[ strlen( buf ) - 1 ] = 0;
185
ri.Printf( PRINT_ALL, "Available modes: '%s'\n", buf );
186
ri.Cvar_Set( "r_availableModes", buf );
195
static int GLimp_SetMode( int mode, qboolean fullscreen )
197
const char* glstring;
199
int colorbits, depthbits, stencilbits;
200
int tcolorbits, tdepthbits, tstencilbits;
202
SDL_Surface *vidscreen = NULL;
203
Uint32 flags = SDL_OPENGL;
204
const SDL_VideoInfo *videoInfo;
206
ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
208
if( displayAspect == 0.0f )
210
#if !SDL_VERSION_ATLEAST(1, 2, 10)
211
// 1.2.10 is needed to get the desktop resolution
212
displayAspect = 4.0f / 3.0f;
213
#elif MINSDL_PATCH >= 10
214
# error Ifdeffery no longer necessary, please remove
216
// Guess the display aspect ratio through the desktop resolution
217
// by assuming (relatively safely) that it is set at or close to
218
// the display's native aspect ratio
219
videoInfo = SDL_GetVideoInfo( );
220
displayAspect = (float)videoInfo->current_w / (float)videoInfo->current_h;
223
ri.Printf( PRINT_ALL, "Estimated display aspect: %.3f\n", displayAspect );
226
ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
228
if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
230
ri.Printf( PRINT_ALL, " invalid mode\n" );
231
return RSERR_INVALID_MODE;
233
ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
237
flags |= SDL_FULLSCREEN;
238
glConfig.isFullscreen = qtrue;
241
glConfig.isFullscreen = qfalse;
243
if (!r_colorbits->value)
246
colorbits = r_colorbits->value;
248
if (!r_depthbits->value)
251
depthbits = r_depthbits->value;
252
stencilbits = r_stencilbits->value;
254
for (i = 0; i < 16; i++)
257
// 1 - minus colorbits
258
// 2 - minus depthbits
260
if ((i % 4) == 0 && i)
272
else if (depthbits == 16)
275
if (stencilbits == 24)
277
else if (stencilbits == 16)
282
tcolorbits = colorbits;
283
tdepthbits = depthbits;
284
tstencilbits = stencilbits;
287
{ // reduce colorbits
288
if (tcolorbits == 24)
293
{ // reduce depthbits
294
if (tdepthbits == 24)
296
else if (tdepthbits == 16)
301
{ // reduce stencilbits
302
if (tstencilbits == 24)
304
else if (tstencilbits == 16)
311
if (tcolorbits == 24)
314
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, sdlcolorbits );
315
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, sdlcolorbits );
316
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, sdlcolorbits );
317
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, tdepthbits );
318
SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, tstencilbits );
320
/*Sago: We don't allow stereo view yet
321
if(r_stereoEnabled->integer)
323
glConfig.stereoEnabled = qtrue;
324
SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
328
glConfig.stereoEnabled = qfalse;
329
SDL_GL_SetAttribute(SDL_GL_STEREO, 0);
332
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
334
#if 0 // See http://bugzilla.icculus.org/show_bug.cgi?id=3526
335
// If not allowing software GL, demand accelerated
336
if( !r_allowSoftwareGL->integer )
338
if( SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ) < 0 )
340
ri.Printf( PRINT_ALL, "Unable to guarantee accelerated "
341
"visual with libSDL < 1.2.10\n" );
346
if( SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, r_swapInterval->integer ) < 0 )
347
ri.Printf( PRINT_ALL, "r_swapInterval requires libSDL >= 1.2.10\n" );
351
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
352
(void *)CLIENT_WINDOW_ICON.pixel_data,
353
CLIENT_WINDOW_ICON.width,
354
CLIENT_WINDOW_ICON.height,
355
CLIENT_WINDOW_ICON.bytes_per_pixel * 8,
356
CLIENT_WINDOW_ICON.bytes_per_pixel * CLIENT_WINDOW_ICON.width,
357
#ifdef Q3_LITTLE_ENDIAN
358
0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
360
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
364
SDL_WM_SetIcon( icon, NULL );
365
SDL_FreeSurface( icon );
369
SDL_WM_SetCaption(CLIENT_WINDOW_TITLE, CLIENT_WINDOW_MIN_TITLE);
372
if (!(vidscreen = SDL_SetVideoMode(glConfig.vidWidth, glConfig.vidHeight, colorbits, flags)))
374
ri.Printf( PRINT_DEVELOPER, "SDL_SetVideoMode failed: %s\n", SDL_GetError( ) );
378
opengl_context = GLimp_GetCurrentContext();
380
ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
381
sdlcolorbits, sdlcolorbits, sdlcolorbits, tdepthbits, tstencilbits);
383
glConfig.colorBits = tcolorbits;
384
glConfig.depthBits = tdepthbits;
385
glConfig.stencilBits = tstencilbits;
389
GLimp_DetectAvailableModes();
393
ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
394
return RSERR_INVALID_MODE;
399
glstring = (char *) qglGetString (GL_RENDERER);
400
ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
407
GLimp_StartDriverAndSetMode
410
static qboolean GLimp_StartDriverAndSetMode( int mode, qboolean fullscreen )
414
if (!SDL_WasInit(SDL_INIT_VIDEO))
416
ri.Printf( PRINT_ALL, "SDL_Init( SDL_INIT_VIDEO )... ");
417
if (SDL_Init(SDL_INIT_VIDEO) == -1)
419
ri.Printf( PRINT_ALL, "FAILED (%s)\n", SDL_GetError());
422
ri.Printf( PRINT_ALL, "OK\n");
425
if (fullscreen && Cvar_VariableIntegerValue( "in_nograb" ) )
427
ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
428
ri.Cvar_Set( "r_fullscreen", "0" );
429
r_fullscreen->modified = qfalse;
433
err = GLimp_SetMode( mode, fullscreen );
437
case RSERR_INVALID_FULLSCREEN:
438
ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
440
case RSERR_INVALID_MODE:
441
ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
454
static void GLimp_InitExtensions( void )
456
if ( !r_allowExtensions->integer )
458
ri.Printf( PRINT_ALL, "* IGNORING OPENGL EXTENSIONS *\n" );
462
ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
465
if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
467
if ( r_ext_compressed_textures->value )
469
glConfig.textureCompression = TC_S3TC;
470
ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
474
glConfig.textureCompression = TC_NONE;
475
ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
480
glConfig.textureCompression = TC_NONE;
481
ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
484
// GL_EXT_texture_env_add
485
glConfig.textureEnvAddAvailable = qfalse;
486
if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
488
if ( r_ext_texture_env_add->integer )
490
glConfig.textureEnvAddAvailable = qtrue;
491
ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
495
glConfig.textureEnvAddAvailable = qfalse;
496
ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
501
ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
504
// GL_ARB_multitexture
505
qglMultiTexCoord2fARB = NULL;
506
qglActiveTextureARB = NULL;
507
qglClientActiveTextureARB = NULL;
508
if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
510
if ( r_ext_multitexture->value )
512
qglMultiTexCoord2fARB = SDL_GL_GetProcAddress( "glMultiTexCoord2fARB" );
513
qglActiveTextureARB = SDL_GL_GetProcAddress( "glActiveTextureARB" );
514
qglClientActiveTextureARB = SDL_GL_GetProcAddress( "glClientActiveTextureARB" );
516
if ( qglActiveTextureARB )
519
qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glint );
520
glConfig.numTextureUnits = (int) glint;
521
if ( glConfig.numTextureUnits > 1 )
523
ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
527
qglMultiTexCoord2fARB = NULL;
528
qglActiveTextureARB = NULL;
529
qglClientActiveTextureARB = NULL;
530
ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
536
ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
541
ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
544
// GL_EXT_compiled_vertex_array
545
if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
547
if ( r_ext_compiled_vertex_array->value )
549
ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
550
qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) SDL_GL_GetProcAddress( "glLockArraysEXT" );
551
qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) SDL_GL_GetProcAddress( "glUnlockArraysEXT" );
552
if (!qglLockArraysEXT || !qglUnlockArraysEXT)
554
ri.Error (ERR_FATAL, "bad getprocaddress");
559
ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
564
ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
567
textureFilterAnisotropic = qfalse;
568
if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) )
570
if ( r_ext_texture_filter_anisotropic->integer ) {
571
qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisotropy );
572
if ( maxAnisotropy <= 0 ) {
573
ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" );
578
ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy );
579
textureFilterAnisotropic = qtrue;
584
ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" );
589
ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" );
593
#define R_MODE_FALLBACK 3 // 640 * 480
599
This routine is responsible for initializing the OS specific portions
603
void GLimp_Init( void )
605
qboolean success = qtrue;
607
r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
609
// create the window and set up the context
610
if( !GLimp_StartDriverAndSetMode( r_mode->integer, r_fullscreen->integer ) )
612
if( r_mode->integer != R_MODE_FALLBACK )
614
ri.Printf( PRINT_ALL, "Setting r_mode %d failed, falling back on r_mode %d\n",
615
r_mode->integer, R_MODE_FALLBACK );
616
if( !GLimp_StartDriverAndSetMode( R_MODE_FALLBACK, r_fullscreen->integer ) )
624
ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
626
// This values force the UI to disable driver selection
627
glConfig.driverType = GLDRV_ICD;
628
glConfig.hardwareType = GLHW_GENERIC;
629
glConfig.deviceSupportsGamma = !!( SDL_SetGamma( 1.0f, 1.0f, 1.0f ) >= 0 );
631
// get our config strings
632
Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
633
Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
634
if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
635
glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
636
Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
637
Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
639
// initialize extensions
640
GLimp_InitExtensions( );
642
ri.Cvar_Get( "r_availableModes", "", CVAR_ROM );
644
// This depends on SDL_INIT_VIDEO, hence having it here
655
Responsible for doing a swapbuffers
658
void GLimp_EndFrame( void )
660
// don't flip if drawing to front buffer
661
if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
663
SDL_GL_SwapBuffers();
666
if( r_fullscreen->modified )
669
qboolean sdlToggled = qfalse;
670
SDL_Surface *s = SDL_GetVideoSurface( );
674
// Find out the current state
675
if( s->flags & SDL_FULLSCREEN )
680
if (r_fullscreen->integer && Cvar_VariableIntegerValue( "in_nograb" ))
682
ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
683
ri.Cvar_Set( "r_fullscreen", "0" );
684
r_fullscreen->modified = qfalse;
687
// Is the state we want different from the current state?
688
if( !!r_fullscreen->integer != fullscreen )
689
sdlToggled = SDL_WM_ToggleFullScreen( s );
694
// SDL_WM_ToggleFullScreen didn't work, so do it the slow way
696
Cbuf_AddText( "vid_restart" );
698
r_fullscreen->modified = qfalse;
706
===========================================================
710
===========================================================
714
* I have no idea if this will even work...most platforms don't offer
715
* thread-safe OpenGL libraries, and it looks like the original Linux
716
* code counted on each thread claiming the GL context with glXMakeCurrent(),
717
* which you can't currently do in SDL. We'll just have to hope for the best.
720
static SDL_mutex *smpMutex = NULL;
721
static SDL_cond *renderCommandsEvent = NULL;
722
static SDL_cond *renderCompletedEvent = NULL;
723
static void (*glimpRenderThread)( void ) = NULL;
724
static SDL_Thread *renderThread = NULL;
728
GLimp_ShutdownRenderThread
731
static void GLimp_ShutdownRenderThread(void)
733
if (smpMutex != NULL)
735
SDL_DestroyMutex(smpMutex);
739
if (renderCommandsEvent != NULL)
741
SDL_DestroyCond(renderCommandsEvent);
742
renderCommandsEvent = NULL;
745
if (renderCompletedEvent != NULL)
747
SDL_DestroyCond(renderCompletedEvent);
748
renderCompletedEvent = NULL;
751
glimpRenderThread = NULL;
756
GLimp_RenderThreadWrapper
759
static int GLimp_RenderThreadWrapper( void *arg )
761
Com_Printf( "Render thread starting\n" );
765
GLimp_SetCurrentContext(NULL);
767
Com_Printf( "Render thread terminating\n" );
774
GLimp_SpawnRenderThread
777
qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
779
static qboolean warned = qfalse;
782
Com_Printf("WARNING: You enable r_smp at your own risk!\n");
787
return qfalse; /* better safe than sorry for now. */
790
if (renderThread != NULL) /* hopefully just a zombie at this point... */
792
Com_Printf("Already a render thread? Trying to clean it up...\n");
793
SDL_WaitThread(renderThread, NULL);
795
GLimp_ShutdownRenderThread();
798
smpMutex = SDL_CreateMutex();
799
if (smpMutex == NULL)
801
Com_Printf( "smpMutex creation failed: %s\n", SDL_GetError() );
802
GLimp_ShutdownRenderThread();
806
renderCommandsEvent = SDL_CreateCond();
807
if (renderCommandsEvent == NULL)
809
Com_Printf( "renderCommandsEvent creation failed: %s\n", SDL_GetError() );
810
GLimp_ShutdownRenderThread();
814
renderCompletedEvent = SDL_CreateCond();
815
if (renderCompletedEvent == NULL)
817
Com_Printf( "renderCompletedEvent creation failed: %s\n", SDL_GetError() );
818
GLimp_ShutdownRenderThread();
822
glimpRenderThread = function;
823
renderThread = SDL_CreateThread(GLimp_RenderThreadWrapper, NULL);
824
if ( renderThread == NULL )
826
ri.Printf( PRINT_ALL, "SDL_CreateThread() returned %s", SDL_GetError() );
827
GLimp_ShutdownRenderThread();
832
// tma 01/09/07: don't think this is necessary anyway?
834
// !!! FIXME: No detach API available in SDL!
835
//ret = pthread_detach( renderThread );
837
//ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
844
static volatile void *smpData = NULL;
845
static volatile qboolean smpDataReady;
852
void *GLimp_RendererSleep( void )
856
GLimp_SetCurrentContext(NULL);
858
SDL_LockMutex(smpMutex);
861
smpDataReady = qfalse;
863
// after this, the front end can exit GLimp_FrontEndSleep
864
SDL_CondSignal(renderCompletedEvent);
866
while ( !smpDataReady )
867
SDL_CondWait(renderCommandsEvent, smpMutex);
869
data = (void *)smpData;
871
SDL_UnlockMutex(smpMutex);
873
GLimp_SetCurrentContext(opengl_context);
883
void GLimp_FrontEndSleep( void )
885
SDL_LockMutex(smpMutex);
888
SDL_CondWait(renderCompletedEvent, smpMutex);
890
SDL_UnlockMutex(smpMutex);
892
GLimp_SetCurrentContext(opengl_context);
900
void GLimp_WakeRenderer( void *data )
902
GLimp_SetCurrentContext(NULL);
904
SDL_LockMutex(smpMutex);
906
assert( smpData == NULL );
908
smpDataReady = qtrue;
910
// after this, the renderer can continue through GLimp_RendererSleep
911
SDL_CondSignal(renderCommandsEvent);
913
SDL_UnlockMutex(smpMutex);
919
void GLimp_RenderThreadWrapper( void *arg )
923
qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
925
ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
929
void *GLimp_RendererSleep( void )
934
void GLimp_FrontEndSleep( void )
938
void GLimp_WakeRenderer( void *data )