1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
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.
11
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.
16
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 ===========================================================================
21 */
22
23 #ifdef USE_LOCAL_HEADERS
24 # include "SDL.h"
25 #else
26 # include <SDL.h>
27 #endif
28
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
34 #endif
35
36 #ifdef SMP
37 # ifdef USE_LOCAL_HEADERS
38 # include "SDL_thread.h"
39 # else
40 # include <SDL_thread.h>
41 # endif
42 #endif
43
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <math.h>
48
49 #include "../renderer/tr_local.h"
50 #include "../client/client.h"
51 #include "../sys/sys_local.h"
52 #include "sdl_icon.h"
53
54 /* Just hack it for now. */
55 #ifdef MACOS_X
56 #include <OpenGL/OpenGL.h>
57 typedef CGLContextObj QGLContext;
58 #define GLimp_GetCurrentContext() CGLGetCurrentContext()
59 #define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx)
60 #else
61 typedef void *QGLContext;
62 #define GLimp_GetCurrentContext() (NULL)
63 #define GLimp_SetCurrentContext(ctx)
64 #endif
65
66 static QGLContext opengl_context;
67
68 typedef enum
69 {
70 RSERR_OK,
71
72 RSERR_INVALID_FULLSCREEN,
73 RSERR_INVALID_MODE,
74
75 RSERR_UNKNOWN
76 } rserr_t;
77
78 static SDL_Surface *screen = NULL;
79
80 cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obtained
81
82 void (APIENTRYP qglActiveTextureARB) (GLenum texture);
83 void (APIENTRYP qglClientActiveTextureARB) (GLenum texture);
84 void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t);
85
86 void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count);
87 void (APIENTRYP qglUnlockArraysEXT) (void);
88
89 /*
90 ===============
91 GLimp_Shutdown
92 ===============
93 */
GLimp_Shutdown(void)94 void GLimp_Shutdown( void )
95 {
96 IN_Shutdown();
97
98 SDL_QuitSubSystem( SDL_INIT_VIDEO );
99 screen = NULL;
100
101 Com_Memset( &glConfig, 0, sizeof( glConfig ) );
102 Com_Memset( &glState, 0, sizeof( glState ) );
103 }
104
105 /*
106 ===============
107 GLimp_LogComment
108 ===============
109 */
GLimp_LogComment(char * comment)110 void GLimp_LogComment( char *comment )
111 {
112 }
113
114 /*
115 ===============
116 GLimp_CompareModes
117 ===============
118 */
GLimp_CompareModes(const void * a,const void * b)119 static int GLimp_CompareModes( const void *a, const void *b )
120 {
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;
127
128 if( aspectDiffsDiff > ASPECT_EPSILON )
129 return 1;
130 else if( aspectDiffsDiff < -ASPECT_EPSILON )
131 return -1;
132 else
133 {
134 if( modeA->w == modeB->w )
135 return modeA->h - modeB->h;
136 else
137 return modeA->w - modeB->w;
138 }
139 }
140
141 /*
142 ===============
143 GLimp_DetectAvailableModes
144 ===============
145 */
GLimp_DetectAvailableModes(void)146 static void GLimp_DetectAvailableModes(void)
147 {
148 char buf[ MAX_STRING_CHARS ] = { 0 };
149 SDL_Rect **modes;
150 int numModes;
151 int i;
152
153 modes = SDL_ListModes( NULL, SDL_OPENGL | SDL_FULLSCREEN );
154
155 if( !modes )
156 {
157 ri.Printf( PRINT_WARNING, "Can't get list of available modes\n" );
158 return;
159 }
160
161 if( modes == (SDL_Rect **)-1 )
162 {
163 ri.Printf( PRINT_ALL, "Display supports any resolution\n" );
164 return; // can set any resolution
165 }
166
167 for( numModes = 0; modes[ numModes ]; numModes++ );
168
169 if(numModes > 1)
170 qsort( modes+1, numModes-1, sizeof( SDL_Rect* ), GLimp_CompareModes );
171
172 for( i = 0; i < numModes; i++ )
173 {
174 const char *newModeString = va( "%ux%u ", modes[ i ]->w, modes[ i ]->h );
175
176 if( strlen( newModeString ) < (int)sizeof( buf ) - strlen( buf ) )
177 Q_strcat( buf, sizeof( buf ), newModeString );
178 else
179 ri.Printf( PRINT_WARNING, "Skipping mode %ux%x, buffer too small\n", modes[i]->w, modes[i]->h );
180 }
181
182 if( *buf )
183 {
184 buf[ strlen( buf ) - 1 ] = 0;
185 ri.Printf( PRINT_ALL, "Available modes: '%s'\n", buf );
186 ri.Cvar_Set( "r_availableModes", buf );
187 }
188 }
189
190 /*
191 ===============
192 GLimp_SetMode
193 ===============
194 */
GLimp_SetMode(int mode,qboolean fullscreen)195 static int GLimp_SetMode( int mode, qboolean fullscreen )
196 {
197 const char* glstring;
198 int sdlcolorbits;
199 int colorbits, depthbits, stencilbits;
200 int tcolorbits, tdepthbits, tstencilbits;
201 int i = 0;
202 SDL_Surface *vidscreen = NULL;
203 Uint32 flags = SDL_OPENGL;
204 const SDL_VideoInfo *videoInfo;
205
206 ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
207
208 if( displayAspect == 0.0f )
209 {
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
215 #else
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;
221 #endif
222
223 ri.Printf( PRINT_ALL, "Estimated display aspect: %.3f\n", displayAspect );
224 }
225
226 ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
227
228 if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
229 {
230 ri.Printf( PRINT_ALL, " invalid mode\n" );
231 return RSERR_INVALID_MODE;
232 }
233 ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
234
235 if (fullscreen)
236 {
237 flags |= SDL_FULLSCREEN;
238 glConfig.isFullscreen = qtrue;
239 }
240 else
241 glConfig.isFullscreen = qfalse;
242
243 if (!r_colorbits->value)
244 colorbits = 24;
245 else
246 colorbits = r_colorbits->value;
247
248 if (!r_depthbits->value)
249 depthbits = 24;
250 else
251 depthbits = r_depthbits->value;
252 stencilbits = r_stencilbits->value;
253
254 for (i = 0; i < 16; i++)
255 {
256 // 0 - default
257 // 1 - minus colorbits
258 // 2 - minus depthbits
259 // 3 - minus stencil
260 if ((i % 4) == 0 && i)
261 {
262 // one pass, reduce
263 switch (i / 4)
264 {
265 case 2 :
266 if (colorbits == 24)
267 colorbits = 16;
268 break;
269 case 1 :
270 if (depthbits == 24)
271 depthbits = 16;
272 else if (depthbits == 16)
273 depthbits = 8;
274 case 3 :
275 if (stencilbits == 24)
276 stencilbits = 16;
277 else if (stencilbits == 16)
278 stencilbits = 8;
279 }
280 }
281
282 tcolorbits = colorbits;
283 tdepthbits = depthbits;
284 tstencilbits = stencilbits;
285
286 if ((i % 4) == 3)
287 { // reduce colorbits
288 if (tcolorbits == 24)
289 tcolorbits = 16;
290 }
291
292 if ((i % 4) == 2)
293 { // reduce depthbits
294 if (tdepthbits == 24)
295 tdepthbits = 16;
296 else if (tdepthbits == 16)
297 tdepthbits = 8;
298 }
299
300 if ((i % 4) == 1)
301 { // reduce stencilbits
302 if (tstencilbits == 24)
303 tstencilbits = 16;
304 else if (tstencilbits == 16)
305 tstencilbits = 8;
306 else
307 tstencilbits = 0;
308 }
309
310 sdlcolorbits = 4;
311 if (tcolorbits == 24)
312 sdlcolorbits = 8;
313
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 );
319
320 if(r_stereoEnabled->integer)
321 {
322 glConfig.stereoEnabled = qtrue;
323 SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
324 }
325 else
326 {
327 glConfig.stereoEnabled = qfalse;
328 SDL_GL_SetAttribute(SDL_GL_STEREO, 0);
329 }
330
331 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
332
333 #if 0 // See http://bugzilla.icculus.org/show_bug.cgi?id=3526
334 // If not allowing software GL, demand accelerated
335 if( !r_allowSoftwareGL->integer )
336 {
337 if( SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ) < 0 )
338 {
339 ri.Printf( PRINT_ALL, "Unable to guarantee accelerated "
340 "visual with libSDL < 1.2.10\n" );
341 }
342 }
343 #endif
344
345 if( SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, r_swapInterval->integer ) < 0 )
346 ri.Printf( PRINT_ALL, "r_swapInterval requires libSDL >= 1.2.10\n" );
347
348 #ifdef USE_ICON
349 {
350 SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
351 (void *)CLIENT_WINDOW_ICON.pixel_data,
352 CLIENT_WINDOW_ICON.width,
353 CLIENT_WINDOW_ICON.height,
354 CLIENT_WINDOW_ICON.bytes_per_pixel * 8,
355 CLIENT_WINDOW_ICON.bytes_per_pixel * CLIENT_WINDOW_ICON.width,
356 #ifdef Q3_LITTLE_ENDIAN
357 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
358 #else
359 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
360 #endif
361 );
362
363 SDL_WM_SetIcon( icon, NULL );
364 SDL_FreeSurface( icon );
365 }
366 #endif
367
368 SDL_WM_SetCaption(CLIENT_WINDOW_TITLE, CLIENT_WINDOW_MIN_TITLE);
369 SDL_ShowCursor(0);
370
371 if (!(vidscreen = SDL_SetVideoMode(glConfig.vidWidth, glConfig.vidHeight, colorbits, flags)))
372 {
373 ri.Printf( PRINT_DEVELOPER, "SDL_SetVideoMode failed: %s\n", SDL_GetError( ) );
374 continue;
375 }
376
377 opengl_context = GLimp_GetCurrentContext();
378
379 ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
380 sdlcolorbits, sdlcolorbits, sdlcolorbits, tdepthbits, tstencilbits);
381
382 glConfig.colorBits = tcolorbits;
383 glConfig.depthBits = tdepthbits;
384 glConfig.stencilBits = tstencilbits;
385 break;
386 }
387
388 GLimp_DetectAvailableModes();
389
390 if (!vidscreen)
391 {
392 ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
393 return RSERR_INVALID_MODE;
394 }
395
396 screen = vidscreen;
397
398 glstring = (char *) qglGetString (GL_RENDERER);
399 ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
400
401 return RSERR_OK;
402 }
403
404 /*
405 ===============
406 GLimp_StartDriverAndSetMode
407 ===============
408 */
GLimp_StartDriverAndSetMode(int mode,qboolean fullscreen)409 static qboolean GLimp_StartDriverAndSetMode( int mode, qboolean fullscreen )
410 {
411 rserr_t err;
412
413 if (!SDL_WasInit(SDL_INIT_VIDEO))
414 {
415 ri.Printf( PRINT_ALL, "SDL_Init( SDL_INIT_VIDEO )... ");
416 if (SDL_Init(SDL_INIT_VIDEO) == -1)
417 {
418 ri.Printf( PRINT_ALL, "FAILED (%s)\n", SDL_GetError());
419 return qfalse;
420 }
421 ri.Printf( PRINT_ALL, "OK\n");
422 }
423
424 if (fullscreen && Cvar_VariableIntegerValue( "in_nograb" ) )
425 {
426 ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
427 ri.Cvar_Set( "r_fullscreen", "0" );
428 r_fullscreen->modified = qfalse;
429 fullscreen = qfalse;
430 }
431
432 err = GLimp_SetMode( mode, fullscreen );
433
434 switch ( err )
435 {
436 case RSERR_INVALID_FULLSCREEN:
437 ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
438 return qfalse;
439 case RSERR_INVALID_MODE:
440 ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
441 return qfalse;
442 default:
443 break;
444 }
445
446 return qtrue;
447 }
448 /*
449 ===============
450 GLimp_InitExtensions
451 ===============
452 */
GLimp_InitExtensions(void)453 static void GLimp_InitExtensions( void )
454 {
455 if ( !r_allowExtensions->integer )
456 {
457 ri.Printf( PRINT_ALL, "* IGNORING OPENGL EXTENSIONS *\n" );
458 return;
459 }
460
461 ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
462
463 // GL_S3_s3tc
464 if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
465 {
466 if ( r_ext_compressed_textures->value )
467 {
468 glConfig.textureCompression = TC_S3TC;
469 ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
470 }
471 else
472 {
473 glConfig.textureCompression = TC_NONE;
474 ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
475 }
476 }
477 else
478 {
479 glConfig.textureCompression = TC_NONE;
480 ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
481 }
482
483 // GL_EXT_texture_env_add
484 glConfig.textureEnvAddAvailable = qfalse;
485 if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
486 {
487 if ( r_ext_texture_env_add->integer )
488 {
489 glConfig.textureEnvAddAvailable = qtrue;
490 ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
491 }
492 else
493 {
494 glConfig.textureEnvAddAvailable = qfalse;
495 ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
496 }
497 }
498 else
499 {
500 ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
501 }
502
503 // GL_ARB_multitexture
504 qglMultiTexCoord2fARB = NULL;
505 qglActiveTextureARB = NULL;
506 qglClientActiveTextureARB = NULL;
507 if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
508 {
509 if ( r_ext_multitexture->value )
510 {
511 qglMultiTexCoord2fARB = SDL_GL_GetProcAddress( "glMultiTexCoord2fARB" );
512 qglActiveTextureARB = SDL_GL_GetProcAddress( "glActiveTextureARB" );
513 qglClientActiveTextureARB = SDL_GL_GetProcAddress( "glClientActiveTextureARB" );
514
515 if ( qglActiveTextureARB )
516 {
517 GLint glint = 0;
518 qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glint );
519 glConfig.numTextureUnits = (int) glint;
520 if ( glConfig.numTextureUnits > 1 )
521 {
522 ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
523 }
524 else
525 {
526 qglMultiTexCoord2fARB = NULL;
527 qglActiveTextureARB = NULL;
528 qglClientActiveTextureARB = NULL;
529 ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
530 }
531 }
532 }
533 else
534 {
535 ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
536 }
537 }
538 else
539 {
540 ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
541 }
542
543 // GL_EXT_compiled_vertex_array
544 if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
545 {
546 if ( r_ext_compiled_vertex_array->value )
547 {
548 ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
549 qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) SDL_GL_GetProcAddress( "glLockArraysEXT" );
550 qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) SDL_GL_GetProcAddress( "glUnlockArraysEXT" );
551 if (!qglLockArraysEXT || !qglUnlockArraysEXT)
552 {
553 ri.Error (ERR_FATAL, "bad getprocaddress");
554 }
555 }
556 else
557 {
558 ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
559 }
560 }
561 else
562 {
563 ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
564 }
565
566 textureFilterAnisotropic = qfalse;
567 if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) )
568 {
569 if ( r_ext_texture_filter_anisotropic->integer ) {
570 qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisotropy );
571 if ( maxAnisotropy <= 0 ) {
572 ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" );
573 maxAnisotropy = 0;
574 }
575 else
576 {
577 ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy );
578 textureFilterAnisotropic = qtrue;
579 }
580 }
581 else
582 {
583 ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" );
584 }
585 }
586 else
587 {
588 ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" );
589 }
590 }
591
592 #define R_MODE_FALLBACK 3 // 640 * 480
593
594 /*
595 ===============
596 GLimp_Init
597
598 This routine is responsible for initializing the OS specific portions
599 of OpenGL
600 ===============
601 */
GLimp_Init(void)602 void GLimp_Init( void )
603 {
604 qboolean success = qtrue;
605
606 r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
607
608 // create the window and set up the context
609 if( !GLimp_StartDriverAndSetMode( r_mode->integer, r_fullscreen->integer ) )
610 {
611 if( r_mode->integer != R_MODE_FALLBACK )
612 {
613 ri.Printf( PRINT_ALL, "Setting r_mode %d failed, falling back on r_mode %d\n",
614 r_mode->integer, R_MODE_FALLBACK );
615 if( !GLimp_StartDriverAndSetMode( R_MODE_FALLBACK, r_fullscreen->integer ) )
616 success = qfalse;
617 }
618 else
619 success = qfalse;
620 }
621
622 if( !success )
623 ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
624
625 // This values force the UI to disable driver selection
626 glConfig.driverType = GLDRV_ICD;
627 glConfig.hardwareType = GLHW_GENERIC;
628 glConfig.deviceSupportsGamma = !!( SDL_SetGamma( 1.0f, 1.0f, 1.0f ) >= 0 );
629
630 // get our config strings
631 Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
632 Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
633 if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
634 glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
635 Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
636 Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
637
638 // initialize extensions
639 GLimp_InitExtensions( );
640
641 ri.Cvar_Get( "r_availableModes", "", CVAR_ROM );
642
643 // This depends on SDL_INIT_VIDEO, hence having it here
644 IN_Init( );
645
646 return;
647 }
648
649
650 /*
651 ===============
652 GLimp_EndFrame
653
654 Responsible for doing a swapbuffers
655 ===============
656 */
GLimp_EndFrame(void)657 void GLimp_EndFrame( void )
658 {
659 // don't flip if drawing to front buffer
660 if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
661 {
662 SDL_GL_SwapBuffers();
663 }
664
665 if( r_fullscreen->modified )
666 {
667 qboolean fullscreen;
668 qboolean sdlToggled = qfalse;
669 SDL_Surface *s = SDL_GetVideoSurface( );
670
671 if( s )
672 {
673 // Find out the current state
674 if( s->flags & SDL_FULLSCREEN )
675 fullscreen = qtrue;
676 else
677 fullscreen = qfalse;
678
679 if (r_fullscreen->integer && Cvar_VariableIntegerValue( "in_nograb" ))
680 {
681 ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
682 ri.Cvar_Set( "r_fullscreen", "0" );
683 r_fullscreen->modified = qfalse;
684 }
685
686 // Is the state we want different from the current state?
687 if( !!r_fullscreen->integer != fullscreen )
688 sdlToggled = SDL_WM_ToggleFullScreen( s );
689 else
690 sdlToggled = qtrue;
691 }
692
693 // SDL_WM_ToggleFullScreen didn't work, so do it the slow way
694 if( !sdlToggled )
695 Cbuf_AddText( "vid_restart" );
696
697 r_fullscreen->modified = qfalse;
698 }
699 }
700
701
702
703 #ifdef SMP
704 /*
705 ===========================================================
706
707 SMP acceleration
708
709 ===========================================================
710 */
711
712 /*
713 * I have no idea if this will even work...most platforms don't offer
714 * thread-safe OpenGL libraries, and it looks like the original Linux
715 * code counted on each thread claiming the GL context with glXMakeCurrent(),
716 * which you can't currently do in SDL. We'll just have to hope for the best.
717 */
718
719 static SDL_mutex *smpMutex = NULL;
720 static SDL_cond *renderCommandsEvent = NULL;
721 static SDL_cond *renderCompletedEvent = NULL;
722 static void (*glimpRenderThread)( void ) = NULL;
723 static SDL_Thread *renderThread = NULL;
724
725 /*
726 ===============
727 GLimp_ShutdownRenderThread
728 ===============
729 */
GLimp_ShutdownRenderThread(void)730 static void GLimp_ShutdownRenderThread(void)
731 {
732 if (smpMutex != NULL)
733 {
734 SDL_DestroyMutex(smpMutex);
735 smpMutex = NULL;
736 }
737
738 if (renderCommandsEvent != NULL)
739 {
740 SDL_DestroyCond(renderCommandsEvent);
741 renderCommandsEvent = NULL;
742 }
743
744 if (renderCompletedEvent != NULL)
745 {
746 SDL_DestroyCond(renderCompletedEvent);
747 renderCompletedEvent = NULL;
748 }
749
750 glimpRenderThread = NULL;
751 }
752
753 /*
754 ===============
755 GLimp_RenderThreadWrapper
756 ===============
757 */
GLimp_RenderThreadWrapper(void * arg)758 static int GLimp_RenderThreadWrapper( void *arg )
759 {
760 Com_Printf( "Render thread starting\n" );
761
762 glimpRenderThread();
763
764 GLimp_SetCurrentContext(NULL);
765
766 Com_Printf( "Render thread terminating\n" );
767
768 return 0;
769 }
770
771 /*
772 ===============
773 GLimp_SpawnRenderThread
774 ===============
775 */
GLimp_SpawnRenderThread(void (* function)(void))776 qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
777 {
778 static qboolean warned = qfalse;
779 if (!warned)
780 {
781 Com_Printf("WARNING: You enable r_smp at your own risk!\n");
782 warned = qtrue;
783 }
784
785 #ifndef MACOS_X
786 return qfalse; /* better safe than sorry for now. */
787 #endif
788
789 if (renderThread != NULL) /* hopefully just a zombie at this point... */
790 {
791 Com_Printf("Already a render thread? Trying to clean it up...\n");
792 SDL_WaitThread(renderThread, NULL);
793 renderThread = NULL;
794 GLimp_ShutdownRenderThread();
795 }
796
797 smpMutex = SDL_CreateMutex();
798 if (smpMutex == NULL)
799 {
800 Com_Printf( "smpMutex creation failed: %s\n", SDL_GetError() );
801 GLimp_ShutdownRenderThread();
802 return qfalse;
803 }
804
805 renderCommandsEvent = SDL_CreateCond();
806 if (renderCommandsEvent == NULL)
807 {
808 Com_Printf( "renderCommandsEvent creation failed: %s\n", SDL_GetError() );
809 GLimp_ShutdownRenderThread();
810 return qfalse;
811 }
812
813 renderCompletedEvent = SDL_CreateCond();
814 if (renderCompletedEvent == NULL)
815 {
816 Com_Printf( "renderCompletedEvent creation failed: %s\n", SDL_GetError() );
817 GLimp_ShutdownRenderThread();
818 return qfalse;
819 }
820
821 glimpRenderThread = function;
822 renderThread = SDL_CreateThread(GLimp_RenderThreadWrapper, NULL);
823 if ( renderThread == NULL )
824 {
825 ri.Printf( PRINT_ALL, "SDL_CreateThread() returned %s", SDL_GetError() );
826 GLimp_ShutdownRenderThread();
827 return qfalse;
828 }
829 else
830 {
831 // tma 01/09/07: don't think this is necessary anyway?
832 //
833 // !!! FIXME: No detach API available in SDL!
834 //ret = pthread_detach( renderThread );
835 //if ( ret ) {
836 //ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
837 //}
838 }
839
840 return qtrue;
841 }
842
843 static volatile void *smpData = NULL;
844 static volatile qboolean smpDataReady;
845
846 /*
847 ===============
848 GLimp_RendererSleep
849 ===============
850 */
GLimp_RendererSleep(void)851 void *GLimp_RendererSleep( void )
852 {
853 void *data = NULL;
854
855 GLimp_SetCurrentContext(NULL);
856
857 SDL_LockMutex(smpMutex);
858 {
859 smpData = NULL;
860 smpDataReady = qfalse;
861
862 // after this, the front end can exit GLimp_FrontEndSleep
863 SDL_CondSignal(renderCompletedEvent);
864
865 while ( !smpDataReady )
866 SDL_CondWait(renderCommandsEvent, smpMutex);
867
868 data = (void *)smpData;
869 }
870 SDL_UnlockMutex(smpMutex);
871
872 GLimp_SetCurrentContext(opengl_context);
873
874 return data;
875 }
876
877 /*
878 ===============
879 GLimp_FrontEndSleep
880 ===============
881 */
GLimp_FrontEndSleep(void)882 void GLimp_FrontEndSleep( void )
883 {
884 SDL_LockMutex(smpMutex);
885 {
886 while ( smpData )
887 SDL_CondWait(renderCompletedEvent, smpMutex);
888 }
889 SDL_UnlockMutex(smpMutex);
890
891 GLimp_SetCurrentContext(opengl_context);
892 }
893
894 /*
895 ===============
896 GLimp_WakeRenderer
897 ===============
898 */
GLimp_WakeRenderer(void * data)899 void GLimp_WakeRenderer( void *data )
900 {
901 GLimp_SetCurrentContext(NULL);
902
903 SDL_LockMutex(smpMutex);
904 {
905 assert( smpData == NULL );
906 smpData = data;
907 smpDataReady = qtrue;
908
909 // after this, the renderer can continue through GLimp_RendererSleep
910 SDL_CondSignal(renderCommandsEvent);
911 }
912 SDL_UnlockMutex(smpMutex);
913 }
914
915 #else
916
917 // No SMP - stubs
GLimp_RenderThreadWrapper(void * arg)918 void GLimp_RenderThreadWrapper( void *arg )
919 {
920 }
921
GLimp_SpawnRenderThread(void (* function)(void))922 qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
923 {
924 ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
925 return qfalse;
926 }
927
GLimp_RendererSleep(void)928 void *GLimp_RendererSleep( void )
929 {
930 return NULL;
931 }
932
GLimp_FrontEndSleep(void)933 void GLimp_FrontEndSleep( void )
934 {
935 }
936
GLimp_WakeRenderer(void * data)937 void GLimp_WakeRenderer( void *data )
938 {
939 }
940
941 #endif
942