5.1. Memory Type Range Registers
Starting with Pentium class processors and including Athlon, K6-2 and other CPUs, there
are Memory Type Range Registers (MTRR) which control how the processor accesses ranges of memory
locations. Basically, it turns many smaller separate writes to the video card into a single
write (a burst). This increases efficiency in writing to the video card and can speed up your
graphics by 250% or more.
See /usr/src/linux/Documentation/mtrr.txt for details. Note that
since this file was written, XFree86 has been patched to automatically detect your video RAM
base address and size and set up the MTRRs.
5.3. About libraries on Linux
A common problem you'll see in gaming is a library file not being found. They're kind of
mysterious and have funny names, so we'll go over libraries on Linux for a bit. There are two
types of libraries, static and dynamic. When you compile a program, by default,
gcc uses dynamic libraries, but you can make gcc use
static libraries instead by using the -static switch. Unless you plan on
compiling your games from source code, you'll mainly be interested in dynamic libraries.
5.3.1. Dynamic libraries
Dynamic libraries, also called a “shared library”, provide object code for
an application while it's running. That is, code gets linked into the executable at run time,
as opposed to compile time. They're analagous to the .dll's used by
Windows. The program responsible for linking code “on the fly” is called
/etc/ld.so, and the dynamic libraries themselves usually end with
.so with a version number, like:
/usr/lib/libSDL.so
/lib/libm.so.3
|
When using gcc, you refer to these libraries by shaving off the
strings lib, .so and all version numbers. So to use
these two libraries, you would pass gcc the -lSDL -lm
options. gcc will then `place a memo inside the executable' that says to
look at the files /usr/lib/libSDL.so and
/lib/libm.so.3 whenever an SDL or math function is used.
5.3.2. Static libraries
In contrast to dynamic libraries which provide code while the application runs, static
libraries contain code which gets linked (inserted) into the program while it's being
compiled. No code gets inserted at run time; the code is completely self-contained. Static
libraries usually end with .a followed by a version number, like:
/usr/lib/libSDL.a
/usr/lib/libm.a
|
The .a files are really an archive of a bunch of
.o (object) files archived together, similar to a tar file. You can use
the nm to see what functions a static library contains:
% nm /usr/lib/libm.a
...
e_atan2.o:
00000000 T __ieee754_atan2
e_atanh.o:
00000000 T __ieee754_atanh
00000000 r half
00000010 r limit
00000018 r ln2_2
...
|
When using gcc, you refer to these libraries by shaving off the
strings “lib”, “.a” and all version numbers. So to use these two
libraries, you would pass gcc the -lSDL -lm options.
gcc will then `bolt on' code from /usr/lib/SDL.a and /usr/lib/libm.a whenever it sees a math function during the
compilation process.
5.3.3. How are library files found
If you compile your own games, your biggest problem with libraries will either be that
gcc can't find a static library or perhaps the library doesn't exist on
your system. When playing games from binary, your library woes will be either be that
ld.so can't find the library or the library doesn't exist on your system.
So it makes some sense to talk about how gcc and ld.so
go about finding libraries in the first place.
gcc looks for libraries in the ``standard system directories'' plus
any directories you specify with the -L option. You can find what these
standard system directories are with gcc -print-search-dirs
ld.so looks to a binary hash contained in a file named
/etc/ld.so.cache for a list of directories that contain available dynamic
libraries. Since it contains binary data, you cannot modify this file directly. However, the
file is generated from a text file /etc/ld.so.conf which you can edit.
This file contains a list of directories that you want ld.so to search for
dynamic libraries. If you want to start putting dynamic libraries in
/home/joecool/privatelibs, you'd add this directory to
/etc/ld.so.conf. Your change doesn't actually make it into
/etc/ld.so.cache until you run ldconfig; once it's
run, ld.so will begin to look for libraries in your private
directory.
Also, even if you just add extra libraries to your system, you must update
ld.so.cache to reflect the presence of the new libraries.
5.3.4. Finding Out What Libraries a Game Depends On
Most commercial Linux games will be dynamically linked against various LGPL libraries,
such as OpenAL or SDL. For these examples, Bioware's NeverWinter Nights <http://nwn.bioware.com> will be used.
To find out what libraries a game uses, we can use the "ldd"
command. Cd to /usr/games/nwn, or wherever you
installed it and take a look at the files. You should see a file called
nwmain; this is the actual game binary. Type "ldd
nwmain" and you'll see:
$ ldd nwmain
linux-gate.so.1 => (0xffffe000)
libm.so.6 => /lib/libm.so.6 (0x40027000)
libpthread.so.0 => /lib/libpthread.so.0 (0x40049000)
libGL.so.1 => /usr/lib/libGL.so.1 (0x4009b000)
libGLU.so.1 => /usr/X11R6/lib/libGLU.so.1 (0x40103000)
libmss.so.6 => not found
libSDL-1.2.so.0 => /usr/lib/libSDL-1.2.so.0 (0x40178000)
libc.so.6 => /lib/libc.so.6 (0x401ff000)
/lib/ld-linux.so.2 (0x40000000)
libGLcore.so.1 => /usr/lib/libGLcore.so.1 (0x40319000)
libnvidia-tls.so.1 => /usr/lib/libnvidia-tls.so.1 (0x409f1000)
libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x409f3000)
libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x40a01000)
libdl.so.2 => /lib/libdl.so.2 (0x40acd000)
libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x40ad1000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x40b88000)
libasound.so.2 => /usr/lib/./libasound.so.2 (0x40b90000)
|
ldd shows all the libraries a dynamic executable relies on, and shows you where they
are. It also "pulls in" the dependencies of the dependencies. For instance, while NWN does
not itself depend on libnvidia-tls.so, the Nvidia supplied
libGL on my system does.
Missing libraries?
In the example above, we can see that nwmain wants libmss.so.6, and the linker cannot find it. Usually, a missing
library is a crash waiting to happen. There is one other thing to consider though: The
majority of games are actually launched by a "wrapper", a shell script that performs some
magic prior to launching the game. In the case of NWN, the wrapper is called
nwn. Let's take a look at that now:
$ less nwn
#!/bin/sh
# This script runs Neverwinter Nights from the current directory
export SDL_MOUSE_RELATIVE=0
export SDL_VIDEO_X11_DGAMOUSE=0
# If you do not wish to use the SDL library included in the package, remove
# ./lib from LD_LIBRARY_PATH
export LD_LIBRARY_PATH=./lib:./miles:$LD_LIBRARY_PATH
./nwmain $@
|
This script sets up some environment variables, then launches the game binary with
whatever command line options we added. The relevant part here is the environment variable
called "LD_LIBRARY_PATH". This is a way of adding to the linkers search path. Try copying the
line to your shell and seeing what happens when you re-run ldd.
$ export LD_LIBRARY_PATH=./lib:./miles:$LD_LIBRARY_PATH
$ ldd nwmain
linux-gate.so.1 => (0xffffe000)
libm.so.6 => /lib/libm.so.6 (0x40027000)
libpthread.so.0 => /lib/libpthread.so.0 (0x40049000)
libGL.so.1 => /usr/lib/libGL.so.1 (0x4009b000)
libGLU.so.1 => /usr/X11R6/lib/libGLU.so.1 (0x40103000)
libmss.so.6 => ./miles/libmss.so.6 (0x40178000)
libSDL-1.2.so.0 => ./lib/libSDL-1.2.so.0 (0x401ec000)
libc.so.6 => /lib/libc.so.6 (0x4025e000)
/lib/ld-linux.so.2 (0x40000000)
libGLcore.so.1 => /usr/lib/libGLcore.so.1 (0x40378000)
libnvidia-tls.so.1 => /usr/lib/libnvidia-tls.so.1 (0x40a50000)
libXext.so.6 => /usr/X11R6/lib/libXext.so.6 (0x40a52000)
libX11.so.6 => /usr/X11R6/lib/libX11.so.6 (0x40a60000)
libdl.so.2 => /lib/libdl.so.2 (0x40b2c000)
libstdc++.so.5 => /usr/lib/libstdc++.so.5 (0x40b30000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x40be7000)
|
As you can see, this gives us slighly different results. The NWN library directories
have been prepended to the search path, so now the linker can find libmss.so.6 in the "./miles"
directory, and also finds the local copy of libSDL first, no longer using the system
copy.
There's another benefit of these scripts: they are easily edited to allow you to provide
your own copy of a library. Any game-supplied copy of a library such as OpenAL or SDL is
likely to be compiled for the lowest common denominator, probably i486 or i686. If you have a
Pentium4 or an AthlonXP, you could compile you own version specifically for your processor.
The compiler will try to optimise the resulting binary, giving some increase in performance.
See the homepage for GCC for more information this at the GCC
site.
Making NWN use your system copy is easy. It says so in the wrapper script! Remove
"./lib:" from the LD_LIBRARY_PATH line, and you're good to go.
Another nice little trick is for games that use OpenAL for their sound output (e.g.
Unreal based games: UT, Postal, Rune, etc.). Since the Open Sound System's (OSS) deprecation
in favour of ALSA, all Linux distributions I've seen now ship with ALSA support as default,
with OSS support actually being supplied via ALSA's compatability modules. The copies of
openal.so distributed with games often do NOT support
ALSA, so making the game use a copy compiled yourself will allow you to use ALSA
natively.