Animation in SDL: Hardware Surfaces

The Simple DirectMedia Layer (SDL) provides three kinds of
surfaces for rendering graphics: software, hardware, and OpenGL. Software
surfaces are stored in the computers main memory. Hardware surfaces are
stored in memory on your video card. OpenGL surfaces are handled in
whatever way OpenGL does things on your system. My previous SDL article
provides basic information about using SDL and details of software
surfaces. This article explores the promise and problems of using
hardware surfaces.

People assume that because hardware buffers are based on
hardware that programs that use them will be faster than programs
that use software buffers. That assumption is often not true. In fact,
some applications will run slower with hardware than with software
buffers. The decision to use a hardware buffer must be based on testing
and not on assumptions. People also seem to forget that the hardware is a
limited resource. Just because you can get enough memory to make the
program fly on your development machine does not mean it will perform as
well on another computer. When they work for you hardware surfaces are
fast, easy to use, and give you smooth animation without tearing and other
unpleasant visual artifacts. When they don't, they can lead to a set of
bewildering problems.

Working With Hardware Surfaces

A cross platform development tool like SDL is designed to let you write
programs that work without change on many different kinds of hardware and
operating systems. Hardware-dependent programming is the opposite of cross
platform development. The conflict between the realities of hardware
dependent development and the goals of SDL are at the root of many of the
problems you may encounter when using hardware surfaces. SDL hardware
surfaces are both cross-platform and hardware dependent.

While writing about SDL hardware surfaces, I have used more weasel
words than a pork-barrel politician the week before election day. I do
that because most features have a few special cases where they don't quite
work the way you expect them to. The special cases come from the many
factors that affect how hardware surfaces really work:

The actual hardware installed in your computer. There are
many ways to build a video card. Your video card may have hundreds of
megabytes of dedicated super fast DDR RAM. Then again, it may not have any
dedicated memory, instead sharing the computer's main memory. Your
hardware may have a blindingly fast graphics accelerator or it may let the
CPU do all the work. Using hardware surfaces doesn't tell you much about
how they will perform on any given system. In fact, they may perform
poorly on a system with a high end graphics accelerator while performing
remarkably well on a system with a weak graphics system.

How your computer talks to your video card. Most computers
talk to the video card over a data bus, like the AGP bus, that is designed
to send data from the CPU to the video card, not the other way
around. It is almost always the case that the CPU can write to the video
card much faster that it can read from the video card. It is usually the
case that the CPU can read and write its main memory faster than it can
read or write video memory. That all means that using
the CPU to copy images (or anything) around in graphics memory is
going to be slow.

The version of the device drivers you are using. Your
hardware may be capable of providing hardware surfaces and hardware
acceleration, but the drivers you are using may not make those abilities
available to user programs. You can write a program that works great on
your computer but that will fail on a similar computer with the same OS
and the same graphics card just because it has a different version of the
device driver.

The way the operating system controls access to hardware.
Operating systems control access to the physical hardware and try to keep
programs from messing with the hardware in ways that can crash the
computer. Because of differences in their design, Windows allows normal
programs to get hardware surfaces while on Linux and other Unix-like
operating systems, a program must have root privileges to access the
hardware.

Having listed so many problems with SDL hardware surfaces, you might
think they are not worth using. However, if you are writing a
two-dimensional game on a platform with good support for SDL hardware
surfaces, they may be the correct choice. You just have to know enough to
know when they are a bad choice.

Using Hardware Surfaces

The easiest way to show the differences between hardware and software
surfaces is to convert the softlines.cpp program, which I
wrote for my last article, from using software to hardware surfaces. The
new program is called hardlines.cpp. Converting the code did
not require me to change many lines of code. Of course, what seemed like
tiny details kept the code form working as expected. The closer you get to
the hardware, the pickier the work gets.

hardlines.cpp has the same sections with the same
functions as softlines.cpp. Working from the top of the
program down, I didn't have to make any changes in the program until I
reached the main() function.

Selecting The Driver

The first change I had to make was to add some Linux specific code just
before the call to SDL_Init():

#ifdef __linux__
putenv("SDL_VIDEODRIVER=dga");
#endif

SDL checks the value of the SDL_VIDEODRIVER environment
variable to decide which driver to use. To get hardware surfaces while
running on Linux under X, you have to specify which driver to use. I've
chosen the DGA driver because the default X11 driver does not support
hardware surfaces. The SDL
FAQ has more information about selecting drivers on Linux and Windows.
There is also a
detailed list of SDL environment variables and their use. The number
of different drivers that you have to choose from is staggering and shows
the range of applications for which SDL could be used.

Setting The Video Mode

The changes are small, but the reasons for the changes aren't. The
options tell SDL that I want a full screen (SDL_FULLSCREEN),
double buffered (SDL_DOUBLEBUF), hardware surface
(SDL_HWSURFACE). The part that isn't obvious is that on my
desktop system if I want a hardware surface, it has to be full screen. I
can't get a hardware surface for a window. This is one of those things
that is operating system and device driver specific. Some systems let you
have a hardware surface for a window. Even if you can get a hardware
surface for a window, you may not be able to get a double buffered
hardware surface for a window.

There are good reasons to refuse a hardware surface for a
window. SDL_SetVideoMode() returns a pointer to an SDL_Surface.
Inside that structure is a pointer to the pixel data for the
surface. Without that pointer you can't draw anything. The demo program
uses that pointer to draw lines. Having a pointer to a window on the
screen means there is a good chance that you can write to any pixel on the
screen, not just the ones in your window. You can probably read from any
pixel on the screen, which creates a nasty security hole. A bug in your
program can scramble the whole desktop, not just your window.

Because you have a pointer to the data in the window, you also have to
worry about what happens when the window is moved, resized, or
obscured. When the window moves, the address of the image data for that
window also moves. If it changes and you use an old copy of the pointer,
your program winds up drawing in the wrong place. If another window
partially covers your window, who is responsible for keeping you from
writing to the covered parts of your window? How does an SDL application
even find out what those are? Double buffering introduces another set of
problems. You may be able to get a hardware surface in a window, and not
be able to get a double buffered surface for that window, because the
entire desktop is not double buffered.

All of these problems can and in fact have been solved many different
ways. By far the easiest solution is just to require that applications
that directly access the screen run as full screen applications. If you
want to use SDL hardware surfaces, assume that your application will have
to run in full screen mode.

To make sure that the program actually got a hardware surface I added
code that tests the surface type right after I set the video mode:

If SDL can't give you what you ask for it will give you what it can. If
it can't give you a hardware surface, SDL will give you a software
surface. We have to check to see if we really got a hardware surface.