abrash-black-book/63-01.md
2013-12-30 20:26:41 +11:00

137 lines
7.3 KiB
Markdown

Chapter 63\
Floating-Point for Real-Time 3-D {#Heading1}
---------------------------------
### Knowing When to Hurl Conventional Math Wisdom Out the Window {#Heading2}
In a crisis, sometimes it's best to go with the first solution that
comes into your head—but not very often.
When I turned 16, my mother had an aging, three-cylinder Saab—not one of
the sporty Saabs that appeared in the late '70s, but a blunt-nosed,
ungainly little wagon that seated up to seven people in sardine-like
comfort, with two of them perched on the gas tank. That was the car I
learned to drive on, and the one I took whenever I wanted to go
somewhere and my mother didn't need it.
My father's car, on the other hand, was a Volvo sedan, only a couple of
years old and easily the classiest car my family had ever owned. To the
best of my recollection, as of New Year's of my senior year, I had never
driven that car. However, I was going to a New Year's party—in fact, I
was going to chauffeur four other people—and for reasons lost in the
mists of time, I was allowed to take the Volvo. So, one crystal clear,
stunningly cold night, I picked up my passengers, who included Robin
Viola, Kathy Smith, Jude Hawron...and Alan, whose last name I'll omit in
case he wants to run for president someday.
The party was at Craig Alexander's house, way out in the middle of
nowhere, and it was a good one. I heard Al Green for the first time,
much beer was consumed (none by me, though), and around 2 a.m., we
decided it was time to head home. So we piled into the Volvo, cranked
the heat up to the max, and set off.
We had gone about five miles when I sensed Alan was trying to tell me
something. As I turned toward him, he said, quite expressively,
"BLEARGH!" and deposited a considerable volume of what had until
recently been beer and chips into his lap.
Mind you, this wasn't just any car Alan was tossing his cookies in—it
was my father's prized Volvo. My reactions were up to the task; without
a moment's hesitation, I shouted, "Do it out the window! Open the
window!" Alan obligingly rolled the window down and, with flawless aim,
sent some more erstwhile beer and chips on its way.
And it was here that I learned that fast decisions are not necessarily
good decisions. A second after the liquid flew out the window, there was
a loud smacking sound, and a yelp from Robin, as the sodden mass hit the
slipstream and splattered along the length of the car. At that point, I
did what I should have done in the first place; I stopped the car so
Alan could get out and finish being sick in peace, while I assessed the
full dimensions of the disaster. Not only was the rear half of the car
on the passenger side—including Robin's window, accounting for the
yelp—covered, but the noxious substance had frozen solid. It looked like
someone had melted an enormous candle, or possibly put cake frosting on
the car.
The next morning, my father was remarkably good-natured about the whole
thing, considering, although I don't remember ever actually driving the
Volvo again. My penance consisted of cleaning the car, no small
punishment considering that I had to take a hair dryer out to our
unheated garage and melt and clean the gunk one small piece at a time.
One thing I learned from this debacle is to pull over very, very quickly
if anyone shows signed of being ill, a bit of wisdom that has proven
useful a suprising number of times over the years. More important,
though, is the lesson that it almost always pays to take at least a few
seconds to size up a crisis situation and choose an effective response,
and that's served me well more times than I can count.
There's a surprisingly close analog to this in programming. Often, when
faced with a problem in his or her code, a programmer's response is to
come up with a solution as quickly as possible and immediately hack it
in. For all but the simplest problems, though, there are side effects
and design issues involved that should be thought through before any
coding is done. I try to think of bugs and other problem situations as
opportunities to reexamine how my code works, as well as chances to
detect and correct structural defects I hadn't previously suspected; in
fact, I'm often able to simplify code as I fix a bug, thanks to the
understanding I gain in the process.
Taking that a step farther, it's useful to reexamine assumptions
periodically even if no bugs are involved. You might be surprised at how
quickly assumptions that once were completely valid can deteriorate.
For example, consider floating-point math.
### Not Your Father's Floating-Point {#Heading3}
Until last year, I had never done any serious floating-point (FP)
optimization, for the perfectly good reason that FP math had never been
fast enough for any of the code I needed to write. It was an article of
faith that FP, while undeniably convenient, because of its automatic
support for constant precision over an enormous range of magnitudes, was
just not fast enough for real-time programming, so I, like pretty much
everyone else doing 3-D, expended a lot of time and effort in making
fixed-point do the job.
That article of faith was true up through the 486, but all the old
assumptions are out the window on the Pentium, for three reasons: faster
FP instructions, a pipelined floating-point unit (FPU), and the magic of
a parallel FXCH. Taken together, these mean that FP addition and
subtraction are nearly as fast as integer operations, and FP
multiplication and division have the potential to be much faster—all
with the range and precision advantages of FP. Better yet, the FPU has
its own set of eight registers, so the use of floating-point can help
relieve pressure on the x86's integer registers, as well.
One effect of all this is that with the Pentium, floating-point on the
x86 has gone from being irrelevant to real-time 3-D to being a key
element. Quake uses FP all the way down into the inner loop of the span
rasterizer, performing several FP operations every 16 pixels.
Floating-point has not only become important for real-time 3-D on the
PC, but will soon become even more crucial. Hardware accelerators will
take care of texture mapping and will increase feasible scene
complexity, meaning the CPU will do less bit-twiddling and will have far
more vertices to transform and project, and far more motion physics and
line-of-sight calculations and the like as well.
By way of getting you started with floating-point for real-time 3-D, in
this chapter I'll examine the basics of Pentium FP optimization, then
look at how some key mathematical techniques for 3-D—dot product, cross
product, transformation, and projection—can be accelerated.
### Pentium Floating-Point Optimization {#Heading4}
I'm going to assume you're already familiar with x86 FP code in general;
for additional information, check out Intel's *Pentium Processor User's
Manual* (order \#241430-001; 1-800-548-4725), a book that you should
have if you're doing Pentium programming of any sort. I'd also recommend
taking a look around [http://www.intel.com](http://www.intel.com).
I'm going to focus on six core instructions in this section: FLD, FST,
FADD, FSUB, FMUL, and FDIV. First, let's look at cycle times for these
instructions. FLD takes 1 cycle; the value is pushed onto the FP stack
and ready for use on the next cycle. FST takes 2 cycles, although when
storing to memory, there's a potential extra cycle that can be lost, as
I'll describe shortly.