2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 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.
26 ===========================================================================
28 #include "../../idlib/precompiled.h"
29 #include "../../renderer/tr_local.h"
32 #include <sys/types.h>
38 # include "libXNVCtrl/NVCtrlLib.h"
41 idCVar sys_videoRam( "sys_videoRam", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Texture memory on the video card (in megabytes) - 0: autodetect", 0, 512 );
44 static int scrnum = 0;
48 bool dga_found = false;
50 static GLXContext ctx = NULL;
52 static bool vidmode_ext = false;
53 static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and minor of XF86VidExtensions
55 static XF86VidModeModeInfo **vidmodes;
56 static int num_vidmodes;
57 static bool vidmode_active = false;
60 static int save_rampsize = 0;
61 static unsigned short *save_red, *save_green, *save_blue;
63 void GLimp_WakeBackEnd(void *a) {
64 common->DPrintf("GLimp_WakeBackEnd stub\n");
68 void GLimp_EnableLogging(bool log) {
72 common->DPrintf("GLimp_EnableLogging - disabled at compile time (ID_GL_HARDLINK)\n");
78 void GLimp_FrontEndSleep() {
79 common->DPrintf("GLimp_FrontEndSleep stub\n");
82 void *GLimp_BackEndSleep() {
83 common->DPrintf("GLimp_BackEndSleep stub\n");
87 bool GLimp_SpawnRenderThread(void (*a) ()) {
88 common->DPrintf("GLimp_SpawnRenderThread stub\n");
92 void GLimp_ActivateContext() {
95 qglXMakeCurrent( dpy, win, ctx );
98 void GLimp_DeactivateContext() {
100 qglXMakeCurrent( dpy, None, NULL );
107 save and restore the original gamma of the system
110 void GLimp_SaveGamma() {
111 if ( save_rampsize ) {
117 XF86VidModeGetGammaRampSize( dpy, scrnum, &save_rampsize);
118 save_red = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
119 save_green = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
120 save_blue = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
121 XF86VidModeGetGammaRamp( dpy, scrnum, save_rampsize, save_red, save_green, save_blue);
128 save and restore the original gamma of the system
131 void GLimp_RestoreGamma() {
135 XF86VidModeSetGammaRamp( dpy, scrnum, save_rampsize, save_red, save_green, save_blue);
137 free(save_red); free(save_green); free(save_blue);
145 gamma ramp is generated by the renderer from r_gamma and r_brightness for 256 elements
146 the size of the gamma ramp can not be changed on X (I need to confirm this)
149 void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned short blue[256]) {
154 XF86VidModeGetGammaRampSize( dpy, scrnum, &size);
155 common->DPrintf("XF86VidModeGetGammaRampSize: %d\n", size);
157 // silly generic resample
159 unsigned short *l_red, *l_green, *l_blue;
160 l_red = (unsigned short *)malloc(size*sizeof(unsigned short));
161 l_green = (unsigned short *)malloc(size*sizeof(unsigned short));
162 l_blue = (unsigned short *)malloc(size*sizeof(unsigned short));
165 for(i=0; i<size-1; i++) {
166 r_f = (float)i*255.0f/(float)(size-1);
167 r_i = (int)floor(r_f);
169 l_red[i] = (int)round((1.0f-r_f)*(float)red[r_i]+r_f*(float)red[r_i+1]);
170 l_green[i] = (int)round((1.0f-r_f)*(float)green[r_i]+r_f*(float)green[r_i+1]);
171 l_blue[i] = (int)round((1.0f-r_f)*(float)blue[r_i]+r_f*(float)blue[r_i+1]);
173 l_red[size-1] = red[255]; l_green[size-1] = green[255]; l_blue[size-1] = blue[255];
174 XF86VidModeSetGammaRamp( dpy, scrnum, size, l_red, l_green, l_blue );
175 free(l_red); free(l_green); free(l_blue);
177 XF86VidModeSetGammaRamp( dpy, scrnum, size, red, green, blue );
182 void GLimp_Shutdown() {
185 Sys_XUninstallGrabs();
187 GLimp_RestoreGamma();
189 qglXDestroyContext( dpy, ctx );
191 #if !defined( ID_GL_HARDLINK )
195 XDestroyWindow( dpy, win );
196 if ( vidmode_active ) {
197 XF86VidModeSwitchToMode( dpy, scrnum, vidmodes[0] );
202 // FIXME: that's going to crash
203 //XCloseDisplay( dpy );
205 vidmode_active = false;
212 void GLimp_SwapBuffers() {
214 qglXSwapBuffers( dpy, win );
219 Check for DGA - update in_dgamouse if needed
222 int dga_MajorVersion = 0, dga_MinorVersion = 0;
226 #if defined( ID_ENABLE_DGA )
227 if ( !XF86DGAQueryVersion( dpy, &dga_MajorVersion, &dga_MinorVersion ) ) {
228 // unable to query, probalby not supported
229 common->Printf( "Failed to detect DGA DirectVideo Mouse\n" );
230 cvarSystem->SetCVarBool( "in_dgamouse", false );
233 common->Printf( "DGA DirectVideo Mouse (Version %d.%d) initialized\n",
234 dga_MajorVersion, dga_MinorVersion );
244 ** the default X error handler exits the application
245 ** I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest)
246 ** but those don't seem to be fatal .. so the default would be to just ignore them
247 ** our implementation mimics the default handler behaviour (not completely cause I'm lazy)
249 int idXErrorHandler(Display * l_dpy, XErrorEvent * ev) {
251 common->Printf( "Fatal X Error:\n" );
252 common->Printf( " Major opcode of failed request: %d\n", ev->request_code );
253 common->Printf( " Minor opcode of failed request: %d\n", ev->minor_code );
254 common->Printf( " Serial number of failed request: %lu\n", ev->serial );
255 XGetErrorText( l_dpy, ev->error_code, buf, 1024 );
256 common->Printf( "%s\n", buf );
260 bool GLimp_OpenDisplay( void ) {
265 if ( cvarSystem->GetCVarInteger( "net_serverDedicated" ) == 1 ) {
266 common->DPrintf( "not opening the display: dedicated server\n" );
270 common->Printf( "Setup X display connection\n" );
272 // that should be the first call into X
273 if ( !XInitThreads() ) {
274 common->Printf("XInitThreads failed\n");
278 // set up our custom error handler for X failures
279 XSetErrorHandler( &idXErrorHandler );
281 if ( !( dpy = XOpenDisplay(NULL) ) ) {
282 common->Printf( "Couldn't open the X display\n" );
285 scrnum = DefaultScreen( dpy );
294 int GLX_Init(glimpParms_t a) {
297 GLX_RED_SIZE, 8, // 1, 2
298 GLX_GREEN_SIZE, 8, // 3, 4
299 GLX_BLUE_SIZE, 8, // 5, 6
300 GLX_DOUBLEBUFFER, // 7
301 GLX_DEPTH_SIZE, 24, // 8, 9
302 GLX_STENCIL_SIZE, 8, // 10, 11
303 GLX_ALPHA_SIZE, 8, // 12, 13
306 // these match in the array
307 #define ATTR_RED_IDX 2
308 #define ATTR_GREEN_IDX 4
309 #define ATTR_BLUE_IDX 6
310 #define ATTR_DEPTH_IDX 9
311 #define ATTR_STENCIL_IDX 11
312 #define ATTR_ALPHA_IDX 13
314 XVisualInfo *visinfo;
315 XSetWindowAttributes attr;
316 XSizeHints sizehints;
318 int colorbits, depthbits, stencilbits;
319 int tcolorbits, tdepthbits, tstencilbits;
320 int actualWidth, actualHeight;
322 const char *glstring;
324 if ( !GLimp_OpenDisplay() ) {
328 common->Printf( "Initializing OpenGL display\n" );
330 root = RootWindow( dpy, scrnum );
332 actualWidth = glConfig.vidWidth;
333 actualHeight = glConfig.vidHeight;
335 // Get video mode list
336 if ( !XF86VidModeQueryVersion( dpy, &vidmode_MajorVersion, &vidmode_MinorVersion ) ) {
338 common->Printf("XFree86-VidModeExtension not available\n");
341 common->Printf("Using XFree86-VidModeExtension Version %d.%d\n",
342 vidmode_MajorVersion, vidmode_MinorVersion);
348 int best_fit, best_dist, dist, x, y;
350 XF86VidModeGetAllModeLines( dpy, scrnum, &num_vidmodes, &vidmodes );
352 // Are we going fullscreen? If so, let's change video mode
353 if ( a.fullScreen ) {
357 for (i = 0; i < num_vidmodes; i++) {
358 if (a.width > vidmodes[i]->hdisplay ||
359 a.height > vidmodes[i]->vdisplay)
362 x = a.width - vidmodes[i]->hdisplay;
363 y = a.height - vidmodes[i]->vdisplay;
364 dist = (x * x) + (y * y);
365 if (dist < best_dist) {
371 if (best_fit != -1) {
372 actualWidth = vidmodes[best_fit]->hdisplay;
373 actualHeight = vidmodes[best_fit]->vdisplay;
375 // change to the mode
376 XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
377 vidmode_active = true;
379 // Move the viewport to top left
381 XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
383 common->Printf( "Free86-VidModeExtension Activated at %dx%d\n", actualWidth, actualHeight );
386 a.fullScreen = false;
387 common->Printf( "Free86-VidModeExtension: No acceptable modes found\n" );
390 common->Printf( "XFree86-VidModeExtension: not fullscreen, ignored\n" );
393 // color, depth and stencil
398 for (i = 0; i < 16; i++) {
400 // 1 - minus colorbits
401 // 2 - minus depthbits
403 if ((i % 4) == 0 && i) {
413 else if (depthbits == 16)
416 if (stencilbits == 24)
418 else if (stencilbits == 16)
423 tcolorbits = colorbits;
424 tdepthbits = depthbits;
425 tstencilbits = stencilbits;
427 if ((i % 4) == 3) { // reduce colorbits
428 if (tcolorbits == 24)
432 if ((i % 4) == 2) { // reduce depthbits
433 if (tdepthbits == 24)
435 else if (tdepthbits == 16)
439 if ((i % 4) == 1) { // reduce stencilbits
440 if (tstencilbits == 24)
442 else if (tstencilbits == 16)
448 if (tcolorbits == 24) {
449 attrib[ATTR_RED_IDX] = 8;
450 attrib[ATTR_GREEN_IDX] = 8;
451 attrib[ATTR_BLUE_IDX] = 8;
454 attrib[ATTR_RED_IDX] = 4;
455 attrib[ATTR_GREEN_IDX] = 4;
456 attrib[ATTR_BLUE_IDX] = 4;
459 attrib[ATTR_DEPTH_IDX] = tdepthbits; // default to 24 depth
460 attrib[ATTR_STENCIL_IDX] = tstencilbits;
462 visinfo = qglXChooseVisual(dpy, scrnum, attrib);
467 common->Printf( "Using %d/%d/%d Color bits, %d Alpha bits, %d depth, %d stencil display.\n",
468 attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX],
469 attrib[ATTR_BLUE_IDX], attrib[ATTR_ALPHA_IDX],
470 attrib[ATTR_DEPTH_IDX],
471 attrib[ATTR_STENCIL_IDX]);
473 glConfig.colorBits = tcolorbits;
474 glConfig.depthBits = tdepthbits;
475 glConfig.stencilBits = tstencilbits;
480 common->Printf("Couldn't get a visual\n");
484 attr.background_pixel = BlackPixel(dpy, scrnum);
485 attr.border_pixel = 0;
486 attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
487 attr.event_mask = X_MASK;
488 if (vidmode_active) {
489 mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
490 CWEventMask | CWOverrideRedirect;
491 attr.override_redirect = True;
492 attr.backing_store = NotUseful;
493 attr.save_under = False;
495 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
498 win = XCreateWindow(dpy, root, 0, 0,
499 actualWidth, actualHeight,
500 0, visinfo->depth, InputOutput,
501 visinfo->visual, mask, &attr);
503 XStoreName(dpy, win, GAME_NAME);
505 // don't let the window be resized
506 // FIXME: allow resize (win32 does)
507 sizehints.flags = PMinSize | PMaxSize;
508 sizehints.min_width = sizehints.max_width = actualWidth;
509 sizehints.min_height = sizehints.max_height = actualHeight;
511 XSetWMNormalHints(dpy, win, &sizehints);
513 XMapWindow( dpy, win );
515 if ( vidmode_active ) {
516 XMoveWindow( dpy, win, 0, 0 );
521 ctx = qglXCreateContext(dpy, visinfo, NULL, True);
524 // Free the visinfo after we're done with it
527 qglXMakeCurrent(dpy, win, ctx);
529 glstring = (const char *) qglGetString(GL_RENDERER);
530 common->Printf("GL_RENDERER: %s\n", glstring);
532 glstring = (const char *) qglGetString(GL_EXTENSIONS);
533 common->Printf("GL_EXTENSIONS: %s\n", glstring);
535 // FIXME: here, software GL test
537 glConfig.isFullscreen = a.fullScreen;
539 if ( glConfig.isFullscreen ) {
540 Sys_GrabMouseCursor( true );
550 This is the platform specific OpenGL initialization function. It
551 is responsible for loading OpenGL, initializing it,
552 creating a window of the appropriate size, doing
553 fullscreen manipulations, etc. Its overall responsibility is
554 to make sure that a functional OpenGL subsystem is operating
555 when it returns to the ref.
557 If there is any failure, the renderer will revert back to safe
558 parameters and try again.
561 bool GLimp_Init( glimpParms_t a ) {
563 if ( !GLimp_OpenDisplay() ) {
567 #ifndef ID_GL_HARDLINK
568 if ( !GLimp_dlopen() ) {
585 bool GLimp_SetScreenParms( glimpParms_t parms ) {
593 open your own display connection for the query and close it
594 using the one shared with GLimp_Init is not stable
597 int Sys_GetVideoRam( void ) {
598 static int run_once = 0;
599 int major, minor, value;
607 if ( sys_videoRam.GetInteger() ) {
608 run_once = sys_videoRam.GetInteger();
609 return sys_videoRam.GetInteger();
612 // try a few strategies to guess the amount of video ram
613 common->Printf( "guessing video ram ( use +set sys_videoRam to force ) ..\n" );
614 if ( !GLimp_OpenDisplay( ) ) {
620 // go for nvidia ext first
621 if ( XNVCTRLQueryVersion( l_dpy, &major, &minor ) ) {
622 common->Printf( "found XNVCtrl extension %d.%d\n", major, minor );
623 if ( XNVCTRLIsNvScreen( l_dpy, l_scrnum ) ) {
624 if ( XNVCTRLQueryAttribute( l_dpy, l_scrnum, 0, NV_CTRL_VIDEO_RAM, &value ) ) {
625 run_once = value / 1024;
628 common->Printf( "XNVCtrlQueryAttribute NV_CTRL_VIDEO_RAM failed\n" );
631 common->Printf( "default screen %d is not controlled by NVIDIA driver\n", l_scrnum );
634 // try ATI /proc read ( for the lack of a better option )
636 if ( ( fd = open( "/proc/dri/0/umm", O_RDONLY ) ) != -1 ) {
638 char umm_buf[ 1024 ];
640 if ( ( len = read( fd, umm_buf, 1024 ) ) != -1 ) {
641 // should be way enough to get the full file
642 // grab "free LFB = " line and "free Inv = " lines
643 umm_buf[ len-1 ] = '\0';
645 line = strtok( umm_buf, "\n" );
648 if ( strlen( line ) >= 13 && strstr( line, "max LFB =" ) == line ) {
649 total += atoi( line + 12 );
650 } else if ( strlen( line ) >= 13 && strstr( line, "max Inv =" ) == line ) {
651 total += atoi( line + 12 );
653 line = strtok( NULL, "\n" );
656 run_once = total / 1048576;
657 // round to the lower 16Mb
662 common->Printf( "read /proc/dri/0/umm failed: %s\n", strerror( errno ) );
665 common->Printf( "guess failed, return default low-end VRAM setting ( 64MB VRAM )\n" );