Setting up 3dfx 3d accelerators on Linux boxes

NB. I shall be talking about Linux/x86 in this document. Other CPUs don't seem to be supported by the vendors yet, though at least Linux/Alpha and Linux/PPC should be able to physically drive a 3dfx card.



Hardware

PC hardware now includes a range of nice 3d graphics accelerators. Unfortunately, most of them don't have Linux drivers: specifically, Nvidia claim they don't want to expend effort to support 3d acceleration under Linux (though they do release some 2d acceleration stuff for their chips via. the XFree86 team), and there are as yet no drivers for S3 or ATI chips (though they're pretty poor performers, so there's little reason to buy them anyway).

What the lab has at least one of (and I have one of) are boards based 3dfx's Voodoo range of chips. As far as I can tell, this includes:

For comparative performance stuff, I recommend Tom's hardware guide.

I have no data on whether the Banshee and Rush X servers are 3d-accelerated at the moment or not: my impression is that they're working in 2d, and 3d work is just starting, but I could be completely wrong.

Voodoo2 based boards are (or, more likely, were, given the existence of Voodoo3) made by Diamond, Creative Labs and lots of others, and come in 8Mbyte (4M video memory, 4Mbyte texture memory) and 12Mbyte (4M video, 8M texture) configurations. Voodoo3 based boards are only made by STB (which was recently purchased by 3dfx).

Voodoo2s have a 16 bit colour depth, and 4Mbyte of VRAM, so they can only manage 800x600 resolution (at a maximum resolution of 120Hz). My Creative 3D Blaster claims to be able to manage 1024x768, but I think this is a misprint. In SLI mode, the effective VRAM is doubled, so you can do 1024x768. I believe Banshees and V3s can do above 800x600 (but don't know if they can do 1600x1200).

Voodoo2 chips tend to be driven via. 3dfx's proprietory graphics language, called glide. 3dfx licence an implementation of glide which talks to the Voodoo chips and provides an API on top of which people can implement their own stuff - usually either OpenGL or Direct3D.

All Voodoo2-based cards are basically the same: specifically, they'll all work with glide, as far as I know. Basically they're just boards for the chips to sit on.

There are a number of versions of Glide: 2.46 will drive VoodooI and Rush boards, but Voodoo2 boards require at least 2.53. The current version is 3.0, which drives Voodoo3 and Voodoo Banshee.

One thing to note when installing a Voodoo2 card is that they dissipate a fair amount of heat, and can get quite hot (50-70 degrees is not uncommon), so try to keep them well-ventilated.

For the purposes of this webpage, I'll concentrate on OpenGL over Glide and Direct3D: fairly few people in the Lab write code using Direct3D, and no-one to my knowledge uses Glide.


Device drivers

Note: all the stuff from glide.xxedgexx.com referred to below comes as RPMs only. If you want to install it on a non-RedHat system, you'll have to use rpm2cpio to turn it into a cpio archive, then cpio -id <[filename]> to unpack it. In the case of the device driver, you'll also need to grok the .spec file to figure out where everything should go.

Linux versions of glide are available from http://glide.xxedgexx.com/. Remember to use the right ones: Quake II, for example, is linked with libc5, not libc6, and you must therefore use the libc5 libtexus.so and libglide2x.so when running it.

There is a driver which accelerates parts of Mesa, a free reimplementation of OpenGL, over glide, so you can accelerate your OpenGL programs.

There are three obvious problems:

  1. glide wants exclusive control of the card. This is fine for Voodoo2, but Banshee and Rush are also used to drive the 2d display, so you need an X server built on top of glide. One is available from http://glide.xxedgexx.com/.
  2. Voodoo2s arbitrarily switch the video signal away from the monitor without informing X, and there's no mechanism for handling this kind of notification anyway. This causes problems with I/O devices, and there's no elegant solution. Quake II uses svgalib to handle the keyboard and mouse, but this requires root privelege. Other programs create a raised-to-front X window, or use XServerGrab(). Either way, it's not a good idea to turn on explicit window positioning in your .fvwm2rc.
  3. glide needs root permissions to talk to the card. The solution here is to invent a protection boundary and write a device driver. This has been done, and is available from the usual place.

SLI

The documentation for Glide claims that it will do SLI automagically. I don't have the hardware to test this, though.


Acceleration without root priveleges

Download and install the device driver from http://glide.xxedgexx.com/. The Glide libraries will automatically use it if they can find it.

Actually installing the device driver is a little bit horrible: firstly, if you're using Linux >2.0.x, you'll need to either get the unofficial port or wait for the official port.

Secondly, the only instructions for installation are in the form of a RedHat .spec file. You should check the .spec file anyway, in case something has changed, but the following worked for me:

  1. Use the makefile to compile the 3dfx.o module. If you want messages output to the syslog for things like successful detection of a 3dfx card, remember to add -DDEBUG to CFLAGS.
  2. Shove it wherever you put your modules (usually something like /lib/modules/[kernel version]/misc).
  3. Create /dev/3dfx, character special, major 120, minor 0.
  4. Give the appropriate people read and write access to /dev/3dfx.
  5. Do insmod [wherever]/3dfx.o mtrrs=1, omitting the bit about mtrrs if you don't have them (if the flags line in /proc/cpuinfo contains mtrr you have them).
  6. That's it.
Note: If you try to use acceleration, by setting MESA_GLX_FX, and /dev/3dfx exists but is not accessible, your program will segfault. The solution is either to unset MESA_GLX_FX or gain access to /dev/3dfx.

How to keep your users happy

If you're a sysadmin, just install the driver, glide, and Mesa (do eg. make linux-386-glide-mits (you want pthreads in case someone tries to use it with the native-threaded JDK)), add the appropriate users to a group that can access /dev/3dfx, set the X server to 16bpp (and fiddle to find the right bit settings), and let them get on with it.

If you're feeling nice, you could also install Mesa/GLX (see below), but this is fairly useless with Voodoo2 cards, since it can only do the in-window hack without taking over the entire screen.


Known problems

This is a list of problems I've come across, together with their solutions.

The dynamic linker becomes confused when linking the libc6 libglide2x.so
Symptoms include messages (especially from Java) of the form can't find symbol PCI_write_thingy or can't find glBegin or other GL functions. Falling back to the libc5 glide drivers (which are statically linked) seems to alleviate the problem a little, and restarting your shell (??!) seems to stop the problem where it occurs.
GL4Java (more generally, Java) doesn't work: there's a bug in Mesa (see the patches section of this web page for a partial fix), and the Linux JDK.
I'm working on it. The core problem seems to be GL4Java's rather cavalier use of glXSetContext().

Quake II

Running Quake II with a Voodoo2 board is fairly easy. All the necessary stuff is included with Quake II for Linux and the appropriate (libc5 !) version of glide.

If your machine is libc6-based (ie. RedHat 5.0 or above), you can override your default glide libraries (which will usually be the libc6 glide and be installed in /lib) by putting the libc5 libraries in the directory pointed to by /etc/quake2.conf.

Two ways of running Quake II with OpenGL rendering are supplied: a version of Mesa, and a mini-GL port that uses Glide more directly, but leaves out most of the OpenGL functions that QII doesn't actually use (eg. fog). The latter is supposed to be faster, but crashes on my machine (Creative Labs 3D Blaster II 12Mbyte). YMMV. The Mesa version works fine for me.

To start QII with the Mesa GL driver, just do:

# ./quake2 +set vid_ref gl +set gl_driver libMesaGL.so
And with the GL miniport:

# ./quake2 +set vid_ref gl +set gl_driver lib3dfxgl.so
.. and that's it.

Note that, although you don't need root priveleges to access the 3d card if you're using the device driver, QII still uses svgalib to read the keyboard and mouse.

Attempting to run Quake II in GL mode as a normal user may well result in you being trapped with a perfectly usable system, but no display (because the pass-through has been switched to the 3d card). Running Quake II with GL rendering as root will rectify this situation, and allow you to regain control.


GLX Support

The other way to render accelerated 3d is to accelerate the X server. There's some effort going into this now in XFree86, and SGI have recently released the GLX source code. You can get more information from the XFree86 3d status report page, but this is mostly for the benefit of owners of cards that don't take over the whole screen. GLX support is pretty pointless for Voodoo2 cards (not to mention quite hard to implement). There is still some point in it, though, as it allows you to accelerate programs running on other machines.

I have some very preliminary acceleration support for GLX. Mail Richard.Watts@cl.cam.ac.uk for details.


Developing applications with accelerated 3d

Although the Glide SDK is available if you want it, most people use Mesa, a free implementation of OpenGL. Mesa is pretty good these days (certainly sufficiently good for Quake to use it), so unless you're using some of the more esoteric features of OpenGL, your program should run under it.

In order to link with Mesa, you may need to prepend Mesa to the names of your GL libraries - eg. -lMesaGL rather than -lGL. Or your admin may have made symlinks for you.

When using native threads, remember that the Mesa FX driver is not thread safe, so you'll have to control serialisation of OpenGL commands quite carefully to ensure correct operation.

The following information is taken from the Mesa Glide driver home page. Rush and Banshee cards run a modified X server, and have no further problems. There are basically three rendering configurations for Voodoo2 cards:

GLX and Mesa/X11 happen automatically if neither of the other methods are selected. Note specifically, that unless you opt for full-screen rendering, Mesa may well switch on the Voodoo card, but not actually use it, rendering all the graphics in software and sending them to the accelerator for display. This Is Not Good...

To select full-screen rendering, do:

export MESA_GLX_FX="fullscreen"
To select rendering in a window, start the server in 16bpp mode, ensure that you're using the GLUT/GLX that came with Mesa (not the one that comes with Glide for Win32), link your application with glide2x.so, and set:

export MESA_GLX_FX="window"     # to enable window rendering
export SST_VGA_PASS=1           # to stop video signal switching
export SST_NOSHUTDOWN=1         # to stop video signal switching
Your application can switch between windowed and full-screen 3d rendering by adding -DXMESA to your CFLAGS, and doing something like:

#if defined(FX) && !defined(WIN32)
#include <GL/xmesa.h>

static int fullscreen=1;
#endif

...

/* In the GLUT keyboard event callback */

#if defined(FX) && !defined(WIN32)
case ' ':
fullscreen=(!fullscreen);
XMesaSetFXmode(fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW);
break;
#endif
That'll do output OK. As far as input goes, you're on your own.


Accelerated 3d in Java

Until Java3D rears its ugly head (and even then), your best bet so far is GL4Java. I'll get around to hacking in support for acceleration soon (acceleration per se will work now, but there's no special arrangement for mode-switching or input yet).

Vanilla Mesa 3.0 won't work with GL4Java and hardware acceleration, because the 3dfx driver contains a bug that doesn't properly restore the Mesa context in some situations: the symptom of this is a segmentation fault at the first drawing instruction of your program. Vanilla Mesa 3.0 with 3dfx support also does some nasty things to signals, which Java can live with but doesn't like. See the patches section for some patches against vanilla Mesa 3.0.

You may still have problems with JDK 1.1. This seems to be because JDK 1.1 doesn't do linking quite right: whether this is the fault of the JDK or of ld-linux.so.2 is an open question. The symptom is an inability to load a library at startup due an `unresolved' symbol, usually part of Mesa or Glide. Recompile the offending library, rerun ldconfig, and try again - you may be lucky.

When using native threads, remember that the Mesa FX driver is not thread safe, so you'll have to control serialisation of OpenGL commands quite carefully to ensure correct operation.

rrw has a rudimentary object-based 3d graphics library written in Pizza. Mail Richard.Watts@cl.cam.ac.uk for details.


Patches

The following patch will stop Mesa segfaulting with GL4Java (and possibly some other applications) when MESA_GLX_FX is set:

*** /usr/libs6/Mesa/src/FX/fxapi.c      Thu Aug 27 03:27:42 1998
--- Mesa-3.0/src/FX/fxapi.c     Tue Feb 23 01:45:59 1999
***************
*** 990,995 ****
--- 990,996 ----
      fprintf(stderr,"fxmesa: fxMesaMakeCurrent(fxMesaCurrentCtx==fxMesa) End\n");
  #endif
  
+     gl_make_current(fxMesa->glCtx,fxMesa->glBuffer);
      return;
    }
And the following will make Mesa handle signals slightly better. If you don't want a signal handled, add it to the comma-separated list in MESA_GLX_SIGNONFATAL:

diff --recursive --new-file Mesa-3.0/src/FX/fxapi.c /usr/libs6/Mesa-3.0/src/FX/f
xapi.c
546a547,552
> #if defined(__linux__)
> #define SIGNAL_MAX 32
> struct sigaction signal_old_acts[SIGNAL_MAX];
> int signal_fatal[SIGNAL_MAX];
> #endif
> 
1060c1066,1082
<   fprintf(stderr,"fxmesa: Received a not handled signal %d\n",s);
---
>   
>   /* Because the old handler may abort the application .. */
>   if (signal_fatal[s]) {
>     fprintf(stderr,"fxmesa: Received a fatal signal %d.\n"
>           "If you don't want this signal to be fatal, add it to the"
>           " comma-separated\nlist in MESA_GLX_FX_SIGNONFATAL.\n",s);
>     cleangraphics();
>   }
> 
>   /* Call the previous handler */
>   if (signal_old_acts[s].sa_handler != NULL) {
>     sigset_t os, os2;
> 
>     sigprocmask(SIG_SETMASK, &signal_old_acts[s].sa_mask, os);
>     signal_old_acts[s].sa_handler(s);
>     sigprocmask(SIG_SETMASK, &os, &os2);
>   }
1062,1063c1084,1086
<   cleangraphics();
<   exit(-1);
---
>   if (signal_fatal[s]) {  
>     exit(-1);
>   }
1115a1139,1147
>     {
>       struct sigaction ss;
>       int i;
> 
> 
>       /* Mark all signals fatal */
>       for (i=0;i<SIGNAL_MAX;i++) { 
>       signal_fatal[i] = 1;
>       }
1117,1124c1149,1193
<     signal(SIGINT,cleangraphics_handler);
<     signal(SIGHUP,cleangraphics_handler);
<     signal(SIGPIPE,cleangraphics_handler);
<     signal(SIGFPE,cleangraphics_handler);
<     signal(SIGBUS,cleangraphics_handler);
<     signal(SIGILL,cleangraphics_handler);
<     signal(SIGSEGV,cleangraphics_handler);
<     signal(SIGTERM,cleangraphics_handler);
---
> 
>       
> 
>       if (getenv("MESA_GLX_FX_SIGNONFATAL")) {
>       /* This is a comma-separated list of signal numbers */
>       char *p = getenv("MESA_GLX_FX_SIGNONFATAL");
>       char *q;
>       int x;
>       long y;
> 
>       fprintf(stderr, "SIGNONFATAL - %s\n", p);
> 
>       do {
>         x = strtol(p, &q, 10);
>         if (x < 1 || x > 31) {
>           fprintf(stderr, "fx Driver: invalid signal number %d "
>                   "in MESA_GLX_SIGNONFATAL.\n",x);
>           exit(-1);
>         } else {
>           signal_fatal[x] = 0;
>         }
>         p = q;
>         if (*p && *p != ',') {
>           fprintf(stderr, "fx Driver: signals must be separated by commas "
>                   "in MESA_GLX_SIGFATAL (%c).\n",*p);
>         } else {
>           if (*p) p++;
>         }
>       } while (*p);
>       }
> 
>       ss.sa_handler = cleangraphics_handler;
>       sigfillset(&ss.sa_mask);
>       ss.sa_flags = 0;
>      
>       sigaction(SIGINT, &ss, &signal_old_acts[SIGINT]);
>       sigaction(SIGHUP, &ss, &signal_old_acts[SIGHUP]);
>       sigaction(SIGPIPE, &ss, &signal_old_acts[SIGPIPE]);
>       sigaction(SIGFPE, &ss, &signal_old_acts[SIGFPE]);
>       sigaction(SIGBUS, &ss, &signal_old_acts[SIGBUS]);
>       sigaction(SIGILL, &ss, &signal_old_acts[SIGILL]);
>       sigaction(SIGSEGV, &ss, &signal_old_acts[SIGSEGV]);
>       sigaction(SIGTERM, &ss, &signal_old_acts[SIGTERM]);
> 
>     }

Useful links

Mesa is a free reimplementation of OpenGL that can run completely in software.

The Mesa Glide driver home page.

3dfx make 3d accelerators. They seem to be fairly Linux-friendly, and run a number of newsgroups on their news server: 3dfx.glide.linux, 3dfx.linux.apps.games, 3dfx.linux.apps.other, 3dfx.linux.dev.glide, 3dfx.linux.dev.opengl.

the Glide for Linux page provides Glide drivers and modified X servers for Linux. An unofficial port of the device driver for 2.1.x and 2.2.x kernels can be found here.

The the XFree86 3d status report page.

GL4Java, an LGPLd set of bindings for OpenGL in Java.

Kaffe, a JIT Java compiler. You might even be able to get it to work with GL4Java...


Richard Watts <Richard.Watts@cl.cam.ac.uk>
Last modified: Tue Mar 9 21:46:29 GMT 1999