From 8b63f1f1a5eb73e09015489c03f47a41fa84aa5d Mon Sep 17 00:00:00 2001 From: James Gregory Date: Mon, 6 Jan 2014 22:51:26 +1100 Subject: [PATCH] Remove heading ids, let pandoc generate them --- about.md | 2 +- about_author.md | 2 +- appendix-a.md | 2 +- book-index.md | 2 +- chapter-01.md | 28 ++++++++++++++-------------- chapter-02.md | 18 +++++++++--------- chapter-03.md | 36 ++++++++++++++++++------------------ chapter-04.md | 48 ++++++++++++++++++++++++------------------------ chapter-05.md | 20 ++++++++++---------- chapter-06.md | 12 ++++++------ chapter-07.md | 20 ++++++++++---------- chapter-08.md | 18 +++++++++--------- chapter-09.md | 24 ++++++++++++------------ chapter-10.md | 14 +++++++------- chapter-11.md | 18 +++++++++--------- chapter-12.md | 24 ++++++++++++------------ chapter-13.md | 14 +++++++------- chapter-14.md | 14 +++++++------- chapter-15.md | 12 ++++++------ chapter-16.md | 26 +++++++++++++------------- chapter-17.md | 22 +++++++++++----------- chapter-18.md | 12 ++++++------ chapter-19.md | 22 +++++++++++----------- chapter-20.md | 14 +++++++------- chapter-21.md | 16 ++++++++-------- chapter-22.md | 6 +++--- chapter-23.md | 24 ++++++++++++------------ chapter-24.md | 8 ++++---- chapter-25.md | 18 +++++++++--------- chapter-26.md | 8 ++++---- chapter-27.md | 18 +++++++++--------- chapter-28.md | 10 +++++----- chapter-29.md | 14 +++++++------- chapter-30.md | 24 ++++++++++++------------ chapter-31.md | 16 ++++++++-------- chapter-32.md | 16 ++++++++-------- chapter-33.md | 22 +++++++++++----------- chapter-34.md | 24 ++++++++++++------------ chapter-35.md | 22 +++++++++++----------- chapter-36.md | 10 +++++----- chapter-37.md | 10 +++++----- chapter-38.md | 14 +++++++------- chapter-39.md | 16 ++++++++-------- chapter-40.md | 18 +++++++++--------- chapter-41.md | 6 +++--- chapter-42.md | 12 ++++++------ chapter-43.md | 16 ++++++++-------- chapter-44.md | 10 +++++----- chapter-45.md | 18 +++++++++--------- chapter-46.md | 12 ++++++------ chapter-47.md | 12 ++++++------ chapter-48.md | 12 ++++++------ chapter-49.md | 16 ++++++++-------- chapter-50.md | 20 ++++++++++---------- chapter-51.md | 14 +++++++------- chapter-52.md | 14 +++++++------- chapter-53.md | 14 +++++++------- chapter-54.md | 10 +++++----- chapter-55.md | 8 ++++---- chapter-56.md | 12 ++++++------ chapter-57.md | 12 ++++++------ chapter-58.md | 16 ++++++++-------- chapter-59.md | 24 ++++++++++++------------ chapter-60.md | 16 ++++++++-------- chapter-61.md | 20 ++++++++++---------- chapter-62.md | 20 ++++++++++---------- chapter-63.md | 24 ++++++++++++------------ chapter-64.md | 36 ++++++++++++++++++------------------ chapter-65.md | 18 +++++++++--------- chapter-66.md | 24 ++++++++++++------------ chapter-67.md | 18 +++++++++--------- chapter-68.md | 24 ++++++++++++------------ chapter-69.md | 22 +++++++++++----------- chapter-70.md | 42 +++++++++++++++++++++--------------------- intro.md | 2 +- 75 files changed, 631 insertions(+), 631 deletions(-) diff --git a/about.md b/about.md index ed09034..4dc91cd 100644 --- a/about.md +++ b/about.md @@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel Graphics and Multimedia Development' --- -# Foreword {#Heading1} +# Foreword I got my start programming on Apple II computers at school, and almost all of my early work was on the Apple platform. After graduating, it diff --git a/about_author.md b/about_author.md index 0fe0709..a9c1042 100644 --- a/about_author.md +++ b/about_author.md @@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel Graphics and Multimedia Development' --- -# Acknowledgments {#Heading1} +# Acknowledgments There are many people to thank—because this book was written over many years, in many different settings, an unusually large number of people diff --git a/appendix-a.md b/appendix-a.md index f5cbc6a..ede685e 100644 --- a/appendix-a.md +++ b/appendix-a.md @@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel Graphics and Multimedia Development' --- -# Afterword {#Heading1} +# Afterword If you've followed me this far, you might agree that we've come through some rough country. Still, I'm of the opinion that hard-won knowledge is diff --git a/book-index.md b/book-index.md index 957269a..e629d35 100644 --- a/book-index.md +++ b/book-index.md @@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel Graphics and Multimedia Development' --- -# Index {#Heading1} +# Index `Numbers` diff --git a/chapter-01.md b/chapter-01.md index 63d3d35..04538c2 100644 --- a/chapter-01.md +++ b/chapter-01.md @@ -13,9 +13,9 @@ pages: 004-019 # Part I ## Chapter 1\ - The Best Optimizer Is between Your Ears {#Heading1} + The Best Optimizer Is between Your Ears -### The Human Element of Code Optimization {#Heading2} +### The Human Element of Code Optimization This book is devoted to a topic near and dear to my heart: writing software that pushes PCs to the limit. Given run-of-the-mill software, @@ -51,7 +51,7 @@ this book, we're going to work some of those wonders, starting... ...now. -### Understanding High Performance {#Heading3} +### Understanding High Performance Before we can create high-performance code, we must understand what high performance is. The objective (not always attained) in creating @@ -93,7 +93,7 @@ touch, however. "What's a fast slow program?" you ask. That's a good question, and a brief (true) story is perhaps the best answer. -#### When Fast Isn't Fast {#Heading4} +#### When Fast Isn't Fast In the early 1970s, as the first hand-held calculators were hitting the market, I knew a fellow named Irwin. He was a good student, and was @@ -123,7 +123,7 @@ grand scheme of things—and they scarcely matter at all unless they're used in the context of a good design and a thorough understanding of both the task at hand and the PC. -### Rules for Building High-Performance Code {#Heading5} +### Rules for Building High-Performance Code We've got the following rules for creating high-performance software: @@ -148,7 +148,7 @@ the real world. For my money, examining some actual working code is always a good way to get a handle on programming concepts, so let's look at some of the performance rules in action. -#### Know Where You're Going {#Heading6} +#### Know Where You're Going If we're going to create high-performance code, first we have to know what that code is going to do. As an example, let's write a program that @@ -161,7 +161,7 @@ with the checksum value other than print it out, however; right now we're only interested in generating that checksum value as rapidly as possible. -#### Make a Big Map {#Heading7} +#### Make a Big Map How are we going to generate a checksum value for a specified file? The logical approach is to get the file name, open the file, read the bytes @@ -169,7 +169,7 @@ out of the file, add them together, and print the result. Most of those actions are straightforward; the only tricky part lies in reading the bytes and adding them together. -#### Make Lots of Little Maps {#Heading8} +#### Make Lots of Little Maps Actually, we're only going to make one little map, because we only have one program section that requires much thought—the section that reads @@ -390,7 +390,7 @@ design, optimization just creates fast slow code. Well, then, how are we going to improve our design? Before we can do that, we have to understand what's wrong with the current design. -#### Know the Territory {#Heading9} +#### Know the Territory Just why is Listing 1.1 so slow? In a word: overhead. The C library implements the `read()` function by calling DOS to read the desired @@ -481,7 +481,7 @@ main(int argc, char *argv[]) { } ``` -#### Know When It Matters {#Heading10} +#### Know When It Matters The last section contained a particularly interesting phrase: *the time-critical portions of your code*. Time-critical portions of your @@ -514,7 +514,7 @@ Besides, we don't want to optimize until the design is refined to our satisfaction, and that won't be the case until we've thought about other approaches. -#### Always Consider the Alternatives {#Heading11} +#### Always Consider the Alternatives Listing 1.4 is good, but let's see if there are other—perhaps less obvious—ways to get the same results faster. Let's start by considering @@ -658,7 +658,7 @@ At any rate, Listing 1.5 isn't much more complicated than Listing 1.4—and it's a *lot* faster. Always consider the alternatives; a bit of clever thinking and program redesign can go a long way. -#### Know How to Turn On the Juice {#Heading12} +#### Know How to Turn On the Juice I have said time and again that optimization is pointless until the design is settled. When that time comes, however, optimization can @@ -808,7 +808,7 @@ specific case. All this is basically a way of saying: Know where you're going, know the territory, and know when it matters. -### Where We've Been, What We've Seen {#Heading13} +### Where We've Been, What We've Seen What have we learned? Don't let other people's code—even DOS—do the work for you when speed matters, at least not without knowing what that code @@ -825,7 +825,7 @@ light of Table 1.1, does it? Your organic optimizer matters much more than your compiler's optimizer, and there's always assembly for those usually small sections of code where performance really matters. -#### Where We're Going {#Heading14} +#### Where We're Going This chapter has presented a quick step-by-step overview of the design process. I'm not claiming that this is the only way to create diff --git a/chapter-02.md b/chapter-02.md index 071f9fe..31edf75 100644 --- a/chapter-02.md +++ b/chapter-02.md @@ -11,9 +11,9 @@ pages: 021-030 --- ## Chapter 2\ - A World Apart {#Heading1} + A World Apart -### The Unique Nature of Assembly Language Optimization {#Heading2} +### The Unique Nature of Assembly Language Optimization As I showed in the previous chapter, optimization is by no means always a matter of "dropping into assembly." In fact, in performance tuning @@ -33,7 +33,7 @@ assembly specific dynamics. As usual, the best way to wade in is to present a real-world example. -### Instructions: The Individual versus the Collective {#Heading3} +### Instructions: The Individual versus the Collective Some time ago, I was asked to work over a critical assembly subroutine in order to make it run as fast as possible. The task of the subroutine @@ -97,7 +97,7 @@ fastest...and more. You must also learn to look at your programming problems from a variety of perspectives so that you can put those fast instructions to work in the most effective ways. -### Assembly Is Fundamentally Different {#Heading4} +### Assembly Is Fundamentally Different Is it really so hard as all that to write good assembly code for the PC? Yes! Thanks to the decidedly quirky nature of the x86 family CPUs, @@ -115,7 +115,7 @@ implements a routine to search a list of 100,000 sorted items with a linear rather than binary search will end up with a disappointingly slow program. -#### Transformation Inefficiencies {#Heading5} +#### Transformation Inefficiencies No matter how well an implementation is derived from the corresponding design, however, high-level languages like C/C++ and Pascal inevitably @@ -166,7 +166,7 @@ specification to machine language entirely on his or her own. (The assembler merely handles the *direct* translation from assembly to machine language.) -#### Self-Reliance {#Heading6} +#### Self-Reliance The first part of assembly language optimization, then, is self. An assembler is nothing more than a tool to let you design machine-language @@ -181,7 +181,7 @@ High-level languages handle most of this transparently to the programmer, but in assembly everything is fair—and necessary—game, which brings us to another aspect of assembly optimization: knowledge. -#### Knowledge {#Heading7} +#### Knowledge In the PC world, you can never have enough knowledge, and every item you add to your store will make your programs better. Thorough familiarity @@ -214,7 +214,7 @@ out such knowledge. > Be forewarned, though: No matter how much you learn about programming > the PC in assembly, there's always more to discover. -### The Flexible Mind {#Heading8} +### The Flexible Mind Is the never-ending collection of information all there is to the assembly optimization, then? Hardly. Knowledge is simply a necessary @@ -316,7 +316,7 @@ the context of a solid overall framework unique to each program, and the flexible mind is the key to creating that framework and holding it together. -#### Where to Begin? {#Heading9} +#### Where to Begin? To summarize, the skill of assembly language optimization is a combination of knowledge, perspective, and a way of thought that makes diff --git a/chapter-03.md b/chapter-03.md index fd1f370..2e967ed 100644 --- a/chapter-03.md +++ b/chapter-03.md @@ -11,9 +11,9 @@ pages: 031-073 --- ## Chapter 3\ - Assume Nothing {#Heading1} + Assume Nothing -### Understanding and Using the Zen Timer {#Heading2} +### Understanding and Using the Zen Timer When you're pushing the envelope in writing optimized PC code, you're likely to become more than a little compulsive about finding approaches @@ -40,7 +40,7 @@ with the optimized version of the routine.... It ran slower than the original version! -### The Costs of Ignorance {#Heading3} +### The Costs of Ignorance As diligent as the author had been, he had nonetheless committed a cardinal sin of x86 assembly language programming: He had assumed that @@ -94,7 +94,7 @@ video wait states as well, so the code they discussed was actually have been to run the code to see if snow resulted, since the only true measure of code performance is observing it in action. -### The Zen Timer {#Heading4} +### The Zen Timer Clearly, one key to mastering Zen-class optimization is a tool with which to measure code performance. The most accurate way to measure @@ -558,7 +558,7 @@ Code ends end ``` -#### The Zen Timer Is a Means, Not an End {#Heading5} +#### The Zen Timer Is a Means, Not an End We're going to spend the rest of this chapter seeing what the Zen timer can do, examining how it works, and learning how to use it. I'll be @@ -573,7 +573,7 @@ Consequently, you shouldn't worry if you don't fully grasp the inner workings of the Zen timer. Instead, focus on learning how to *use* it, and you'll be on the right road. -#### Starting the Zen Timer {#Heading6} +#### Starting the Zen Timer `ZTimerOn` is called at the start of a segment of code to be timed. `ZTimerOn` saves the context of the calling code, disables interrupts, @@ -596,7 +596,7 @@ any hardware interrupts to occur during the interval between any call to `ZTimerOn` and the corresponding call to `ZTimerOff`, and should not enable interrupts during that time. -### Time and the PC {#Heading7} +### Time and the PC A second interesting point about `ZTimerOn` is that it may introduce some small inaccuracy into the system clock time whenever it is called. @@ -742,7 +742,7 @@ Nonetheless, it's a good idea to reboot your computer at the end of each session with the Zen timer in order to make sure that the system clock is correct. -### Stopping the Zen Timer {#Heading8} +### Stopping the Zen Timer At some point after `ZTimerOn` is called, `ZTimerOff` must always be called to mark the end of the timing interval. `ZTimerOff` saves the @@ -774,7 +774,7 @@ this chapter, though, we'll see that timer 0 can be stopped after all.) We simply tell the 8253 to latch the current count, and the 8253 does so without breaking stride. -### Reporting Timing Results {#Heading9} +### Reporting Timing Results `ZTimerReport` may be called to display timing results at any time after both `ZTimerOn` and `ZTimerOff` have been called. @@ -829,7 +829,7 @@ You may well want to devise still other approaches better suited to your needs than those I've presented. Go to it! I've just thrown out a few possibilities to get you started. -### Notes on the Zen Timer {#Heading10} +### Notes on the Zen Timer The Zen timer subroutines are designed to be near-called from assembly language code running in the public segment `Code`. The Zen timer @@ -890,7 +890,7 @@ useful—quite the contrary. The Zen timer is an excellent tool for evaluating code performance over the entire spectrum of PC-compatible computers. -### A Sample Use of the Zen Timer {#Heading11} +### A Sample Use of the Zen Timer Listing 3.2 shows a test-bed program for measuring code performance with the Zen timer. This program sets DS equal to CS (for reasons we'll @@ -1114,7 +1114,7 @@ the test-bed program of Listing 3.2, simply insert calls to `ZTimerOn, ZTimerOff`, and `ZTimerReport` in the appropriate places and link PZTIMER to your program. -### The Long-Period Zen Timer {#Heading12} +### The Long-Period Zen Timer With a few exceptions, the Zen timer presented above will serve us well for the remainder of this book since we'll be focusing on relatively @@ -1171,7 +1171,7 @@ major inaccuracy into the system clock time during a single timing run since it leaves interrupts enabled and therefore allows the system clock to update normally. -#### Stopping the Clock {#Heading13} +#### Stopping the Clock There's a potential problem with the long-period Zen timer. The problem is this: In order to measure times longer than 54 ms, we must maintain @@ -1897,7 +1897,7 @@ Finally, please note that the *precision* Zen timer works perfectly well on both PS/2 and non-PS/2 computers. The PS/2 and 8253 considerations we've just discussed apply *only* to the longZen timer. -### Example Use of the Long-Period Zen Timer {#Heading14} +### Example Use of the Long-Period Zen Timer The long-period Zen timer has exactly the same calling interface as the precision Zen timer, and can be used in place of the precision Zen timer @@ -2113,7 +2113,7 @@ PC if you are using MASM, with most of that time spent assembling Listing 3.8. Why? Because MASM is notoriously slow at assembling `REPT` blocks, and the block in Listing 3.8 is repeated 20,000 times. -### Using the Zen Timer from C {#Heading15} +### Using the Zen Timer from C The Zen timer can be used to measure code performance when programming in C—but not right out of the box. As presented earlier, the timer is @@ -2169,7 +2169,7 @@ precision timer, but the long-period timer is very similar. The full listings for the C-callable Zen timers are presented in Chapter K on the companion CD-ROM. -#### Watch Out for Optimizing Assemblers! {#Heading16} +#### Watch Out for Optimizing Assemblers! One important safety tip when modifying the Zen timer for use with large code model C code: Watch out for optimizing assemblers! TASM actually @@ -2221,7 +2221,7 @@ I've tested the changes shown in Figures 3.2 and 3.3 with TASM and Borland C++ 4.0, and also with the latest MASM and Microsoft C/C++ compiler. -#### Further Reading {#Heading17} +#### Further Reading For those of you who wish to pursue the mechanics of code measurement further, one good article about measuring code performance with the 8253 @@ -2242,7 +2242,7 @@ how the Zen timer works. All you really need to know is what the Zen timer can do and how to use it, and we've accomplished that in this chapter. -#### Armed with the Zen Timer, Onward and Upward {#Heading18} +#### Armed with the Zen Timer, Onward and Upward The Zen timer is not perfect. For one thing, the finest resolution to which it can measure an interval is at best about 1µs, a period of time diff --git a/chapter-04.md b/chapter-04.md index e1aaacb..a54f5b9 100644 --- a/chapter-04.md +++ b/chapter-04.md @@ -11,9 +11,9 @@ pages: 075-109 --- ## Chapter 4\ - In the Lair of the Cycle-Eaters {#Heading1} + In the Lair of the Cycle-Eaters -### How the PC Hardware Devours Code Performance {#Heading2} +### How the PC Hardware Devours Code Performance This chapter, adapted from my earlier book, *Zen of Assembly Language* located on the companion CD-ROM, goes right to the heart of my @@ -47,7 +47,7 @@ changed over time, but do take the time to at least skim through this chapter to give yourself a good start on the material in the rest of this book. -### Cycle-Eaters {#Heading3} +### Cycle-Eaters Programming has many levels, ranging from the familiar (high-level languages, DOS calls, and the like) down to the esoteric things that lie @@ -81,7 +81,7 @@ properly improve the performance of our code. Which brings us to cycle-eaters. -### The Nature of Cycle-Eaters {#Heading4} +### The Nature of Cycle-Eaters Cycle-eaters are gremlins that live on the bus or in peripherals (and sometimes within the CPU itself), slowing the performance of PC code so @@ -101,7 +101,7 @@ understand the simplest among them, those that haunted the original the newer generation of cycle-eaters in terms of those ancestral cycle-eaters—but we have to get the groundwork down first. -#### The 8088's Ancestral Cycle-Eaters {#Heading5} +#### The 8088's Ancestral Cycle-Eaters Internally, the 8088 is a 16-bit processor, capable of running at full speed at all times—unless external data is required. External data must @@ -131,7 +131,7 @@ but do read it all even if the going gets a bit tough. Cycle-eaters play a key role in later chapters, so some familiarity with them is highly desirable. -### The 8-Bit Bus Cycle-Eater {#Heading6} +### The 8-Bit Bus Cycle-Eater *Look! Down on the motherboard! It's a 16-bit processor! It's an 8-bit processor! It's...* @@ -194,7 +194,7 @@ a full 16-bit word at a time. The 386SX can process 32 bits (a doubleword) at a time, however, and loses a lot of time fetching that doubleword from memory in two halves. -#### The Impact of the 8-Bit Bus Cycle-Eater {#Heading7} +#### The Impact of the 8-Bit Bus Cycle-Eater One obvious effect of the 8-bit bus cycle-eater is that word-sized accesses to memory operands on the 8088 take 4 cycles longer than @@ -255,7 +255,7 @@ operands. More ominously, as we will see shortly, the 8-bit bus cycle-eater can cause performance problems with other sorts of code as well. -#### What to Do about the 8-Bit Bus Cycle-Eater? {#Heading8} +#### What to Do about the 8-Bit Bus Cycle-Eater? The obvious implication of the 8-bit bus cycle-eater is that byte-sized memory variables should be used whenever possible. After all, the 8088 @@ -426,7 +426,7 @@ and there's one sort of word-sized memory access we haven't discussed yet: instruction fetching. The ugliest manifestation of the 8-bit bus cycle-eater is in fact the prefetch queue cycle-eater. -### The Prefetch Queue Cycle-Eater {#Heading9} +### The Prefetch Queue Cycle-Eater In an 8088 context, here's the prefetch queue cycle-eater in a nutshell: The 8088's 8-bit external data bus keeps the Bus Interface Unit from @@ -492,7 +492,7 @@ external data bus, that's a glaring omission—but, alas, an unavoidable one. Let's look at why the official execution times are wrong, and why that can't be helped. -#### Official Execution Times Are Only Part of the Story {#Heading10} +#### Official Execution Times Are Only Part of the Story The sequence of 5 `SHR` instructions in the last example is 10 bytes long. That means that it can never execute in less than 24 cycles even @@ -538,7 +538,7 @@ wrong, and why Intel can't provide better specifications. You also know now why it is that you must time your code if you want to know how fast it really is. -#### There Is No Such Beast as a True Instruction Execution Time {#Heading11} +#### There Is No Such Beast as a True Instruction Execution Time The effect of the code preceding an instruction on the execution time of that instruction makes the Zen timer trickier to use than you might @@ -738,7 +738,7 @@ executes on the PC just by looking at it! Get used to the idea that execution times are only meaningful in context, learn the rules of thumb in this book, and use the Zen timer to measure your code. -#### Approximating Overall Execution Times {#Heading12} +#### Approximating Overall Execution Times Don't think that because overall instruction execution time is determined by both instruction fetch time and Execution Unit execution @@ -767,7 +767,7 @@ instruction in a particular context to start when the first byte of the instruction is sent to the Execution Unit and end when the first byte of the next instruction is sent to the EU. -#### What to Do about the Prefetch Queue Cycle-Eater? {#Heading13} +#### What to Do about the Prefetch Queue Cycle-Eater? Reducing the impact of the prefetch queue cycle-eater is one of the overriding principles of high-performance assembly code. How can you do @@ -809,7 +809,7 @@ then time your code to see how fast it really is. You should experiment freely, but always remember that actual, measured performance is the bottom line. -#### Holding Up the 8088 {#Heading14} +#### Holding Up the 8088 In this chapter I've taken you further and further into the depths of the PC, telling you again and again that you must understand the @@ -849,7 +849,7 @@ were programming in ignorance. Let's start with DRAM refresh, which affects the performance of every program that runs on the PC. -### Dynamic RAM Refresh: The Invisible Hand {#Heading15} +### Dynamic RAM Refresh: The Invisible Hand Dynamic RAM (DRAM) refresh is sort of an act of God. By that I mean that DRAM refresh invisibly and inexorably steals a certain fraction of all @@ -881,7 +881,7 @@ within 4 µs of the last refresh. Since there's no guarantee that a given program will access each and every DRAM block once every 4 µs, the PC contains special circuitry and programming for providing DRAM refresh. -#### How DRAM Refresh Works in the PC {#Heading16} +#### How DRAM Refresh Works in the PC On the original 8088-based IBM PC, timer 1 of the 8253 timer chip is programmed at power-up to generate a signal once every 72 cycles, or @@ -920,7 +920,7 @@ memory. ![**Figure 4.5**  *The PC bus dynamic RAM (DRAM) refresh.*](images/04-05.jpg) -#### The Impact of DRAM Refresh {#Heading17} +#### The Impact of DRAM Refresh Let's look at examples from opposite ends of the spectrum in terms of the impact of DRAM refresh on code performance. First, consider the @@ -1005,7 +1005,7 @@ ranging as high as 8.33 percent—is far more likely to occur. This is especially true for high-performance assembly code, which uses fast instructions that tend to cause non-stop instruction fetching. -#### What to Do About the DRAM Refresh Cycle-Eater? {#Heading18} +#### What to Do About the DRAM Refresh Cycle-Eater? *Hmmm.* When we discovered the prefetch queue cycle-eater, we learned to use short instructions. When we discovered the 8-bit bus cycle-eater, we @@ -1059,7 +1059,7 @@ two timing results that differ less or more than they seemingly should, that's usually DRAM refresh too. Thanks to DRAM refresh, variations of up to 8.33 percent in PC code performance are par for the course. -### Wait States {#Heading19} +### Wait States Wait states are cycles during which a bus access by the CPU to a device on the PC's bus is temporarily halted by that device while the device @@ -1121,7 +1121,7 @@ the PC. While any adapter *can* insert wait states, in the PC only display adapters do so to the extent that performance is seriously affected. -### The Display Adapter Cycle-Eater {#Heading20} +### The Display Adapter Cycle-Eater Display adapters must serve two masters, and that creates a fundamental performance problem. Master \#1 is the circuitry that drives the display @@ -1212,7 +1212,7 @@ the foreseeable future, and since it is the hardest graphics adapter to wring performance from, we'll restrict our discussion to the VGA (and its close relative, the EGA) for the remainder of this chapter. -#### The Impact of the Display Adapter Cycle-Eater {#Heading21} +#### The Impact of the Display Adapter Cycle-Eater Even on the EGA and VGA, the effect of the display adapter cycle-eater depends on the display mode selected. In text mode, the display adapter @@ -1366,7 +1366,7 @@ PC. Remember, the limited speed of access to a graphics adapter is an inherent characteristic of the adapter, so the fastest computer around can't access display memory one iota faster than the adapter will allow. -#### What to Do about the Display Adapter Cycle-Eater? {#Heading22} +#### What to Do about the Display Adapter Cycle-Eater? What can we do about the display adapter cycle-eater? Well, we can minimize display memory accesses whenever possible. In particular, we @@ -1422,7 +1422,7 @@ significant. There is only one way to discover just how significant the impact of the display adapter cycle-eater is for any particular graphics code, and that is of course to measure the performance of that code. -#### Cycle-Eaters: A Summary {#Heading23} +#### Cycle-Eaters: A Summary We've covered a great deal of sophisticated material in this chapter, so don't feel bad if you haven't understood everything you've read; it will @@ -1446,7 +1446,7 @@ This basic knowledge about cycle-eaters puts you in a good position to understand the results reported by the Zen timer, and that means that you're well on your way to writing high-performance assembler code. -#### What Does It All Mean? {#Heading24} +#### What Does It All Mean? There you have it: life under the programming interface. It's not a particularly pretty picture for the inhabitants of that strange realm diff --git a/chapter-05.md b/chapter-05.md index 243ff93..8cd6c24 100644 --- a/chapter-05.md +++ b/chapter-05.md @@ -11,9 +11,9 @@ pages: 111-123 --- ## Chapter 5\ - Crossing the Border {#Heading1} + Crossing the Border -### Searching Files with Restartable Blocks {#Heading2} +### Searching Files with Restartable Blocks *We just moved.* Those three little words should strike terror into the heart of anyone who owns more than a sleeping bag and a toothbrush. Our @@ -66,7 +66,7 @@ more effort and forethought, but would have paid off handsomely. And with that, let's look at a fairly complex application of restartable blocks. -#### Searching for Text {#Heading3} +#### Searching for Text The application we're going to examine searches a file for a specified string. We'll develop a program that will search the file specified on @@ -112,7 +112,7 @@ all-important inner loop of our searching program, where the program will spend virtually all of its time (aside from the unavoidable disk access overhead). -### Avoiding the String Trap {#Heading4} +### Avoiding the String Trap The easiest approach would be to use a C/C++ library function. The closest match to what we need is `strstr()`, which searches one string @@ -136,7 +136,7 @@ for our application, from unnecessary overhead. > it, and relate that to their performance in the context you're > interested in. -### Brute-Force Techniques {#Heading5} +### Brute-Force Techniques Given that no C/C++ library function meets our needs precisely, an obvious alternative approach is the brute-force technique that uses @@ -169,7 +169,7 @@ match for the first character, stopping to check for a match with the rest of the string *only* when the first character matches, as shown in Figure 5.2. -### Using memchr() {#Heading6} +### Using memchr() There's yet a better way to implement this approach, however. Use the `memchr()` function, which does nothing more or less than find the @@ -201,7 +201,7 @@ Now that we've selected a searching approach, let's integrate it with file handling and searching through multiple blocks. In other words, let's make it restartable. -#### Making a Search Restartable {#Heading7} +#### Making a Search Restartable As it happens, there's no great trick to putting the pieces of this search program together. Basically, we'll read in a buffer of data @@ -407,7 +407,7 @@ main(int argc, char *argv[]) { } ``` -### Interpreting Where the Cycles Go {#Heading8} +### Interpreting Where the Cycles Go To boost the overall performance of Listing 5.1, I would normally convert `SearchForString()` to assembly language at this point. @@ -461,7 +461,7 @@ work. Not likely. -#### Knowing When Assembly Is Pointless {#Heading9} +#### Knowing When Assembly Is Pointless So that's why we're not going to go to assembly language in this example—which is not to say it would never be worth converting the @@ -516,7 +516,7 @@ search engine is by no means fully optimized, it's nonetheless as fast as there's any reason for it to be, given the balance of performance among the components of this program. -### Always Look Where Execution Is Going {#Heading10} +### Always Look Where Execution Is Going I've explained two important lessons: Know when it's worth optimizing further, and use restartable blocks to process large data sets as a diff --git a/chapter-06.md b/chapter-06.md index 71804fe..c4ef9ac 100644 --- a/chapter-06.md +++ b/chapter-06.md @@ -11,9 +11,9 @@ pages: 125-133 --- ## Chapter 6\ - Looking Past Face Value {#Heading1} + Looking Past Face Value -### How Machine Instructions May Do More Than You Think {#Heading2} +### How Machine Instructions May Do More Than You Think I first met Jeff Duntemann at an authors' dinner hosted by *PC Tech Journal* at Fall Comdex, back in 1985. Jeff was already reasonably @@ -95,7 +95,7 @@ disadvantage every time you sit down to program. In short, the x86 family can do much more than you think—if you'll use everything it has to offer. Give it a shot! -#### Memory Addressing and Arithmetic {#Heading3} +#### Memory Addressing and Arithmetic Years ago, I saw a clip on the David Letterman show in which Letterman walked into a store by the name of "Just Lamps" and asked, "So what do @@ -169,7 +169,7 @@ calculations are free on the Pentium, however. I'll discuss 486 performance issues in Chapters 12 and 13, and the Pentium in Chapters 19 through 21. -### Math via Memory Addressing {#Heading4} +### Math via Memory Addressing You're probably not particularly wowed to hear that you can use addressing modes to perform memory addressing arithmetic that would @@ -235,7 +235,7 @@ and Pentium, `LEA` can also be slowed down by addressing interlocks. ![**Figure 6.1**  *Operation of ADD Reg,Reg vs. LEA Reg,{Addr}.*](images/06-01.jpg) -#### The Wonders of LEA on the 386 {#Heading5} +#### The Wonders of LEA on the 386 `LEA` really comes into its own as a "super-ADD" instruction on the 386, 486, and Pentium, where it can take advantage of the enhanced @@ -282,7 +282,7 @@ add edi,offset TableBase when pointing to an entry in a doubly indexed table. -### Multiplication with LEA Using Non-Powers of Two {#Heading6} +### Multiplication with LEA Using Non-Powers of Two Are you impressed yet with all that `LEA` can do on the 386? Believe it or not, one more feature still awaits us. `LEA` can actually diff --git a/chapter-07.md b/chapter-07.md index dd7b4ca..712bef8 100644 --- a/chapter-07.md +++ b/chapter-07.md @@ -11,9 +11,9 @@ pages: 136-148 --- ## Chapter 7\ - Local Optimization {#Heading1} + Local Optimization -### Optimizing Halfway between Algorithms and Cycle Counting {#Heading2} +### Optimizing Halfway between Algorithms and Cycle Counting You might not think it, but there's much to learn about performance programming from the Great Buffalo Sauna Fiasco. To wit: @@ -88,7 +88,7 @@ possible given the capabilities of the x86 family instruction set. And yes, in case you're wondering, the above story is indeed true. Was I there? Let me put it this way: If I were, I'd never admit it! -#### When LOOP Is a Bad Idea {#Heading3} +#### When LOOP Is a Bad Idea Let's examine first an instruction that is less than it appears to be: `LOOP`. There's no mystery about what `LOOP` does; it decrements CX @@ -144,7 +144,7 @@ jz SkipLoop ;If field is 0, don't bother will do just fine and is faster on all processors. Use `JCXZ` only when the Zero flag isn't already set to reflect the status of CX. -### The Lessons of LOOP and JCXZ {#Heading4} +### The Lessons of LOOP and JCXZ What can we learn from `LOOP` and `JCXZ`? First, that a single instruction that is intended to do a complex task is not necessarily @@ -167,7 +167,7 @@ published cycle times are closer to actual execution times on the 386 and 486 than on the 8088, and are reasonably reliable indicators of the relative performance levels of x86 instructions. -#### Avoiding LOOPS of Any Stripe {#Heading5} +#### Avoiding LOOPS of Any Stripe Cycle counting and directly substituting instructions (`DEC CX/JNZ` for `LOOP`, for example) are techniques that belong at the lowest @@ -180,7 +180,7 @@ caught up in counting cycles because that's a small (albeit important) part of the optimization picture, and not the area in which your greatest advantage lies. -### Local Optimization {#Heading6} +### Local Optimization One level at which assembly language programming pays off handsomely is that of *local optimization;* that is, selecting the best *sequence* of @@ -327,7 +327,7 @@ SearchMaxLengthendp end Start ``` -### Unrolling Loops {#Heading7} +### Unrolling Loops Listing 7.2 takes a different tack, unrolling the loop so that four bytes are checked for each `LOOP` performed. The same instructions are @@ -490,7 +490,7 @@ avenues. > as building blocks with unique characteristics rather than as > instructions dedicated to specific tasks. -#### Rotating and Shifting with Tables {#Heading8} +#### Rotating and Shifting with Tables As another example of local optimization, consider the matter of rotating or shifting a mask into position. First, let's look at the @@ -558,7 +558,7 @@ BIT_PATTERN=BIT_PATTERN SHL 1 > optimization rule: Move as much work as possible out of your critical > code by whatever means necessary. -#### NOT Flips Bits—Not Flags {#Heading9} +#### NOT Flips Bits—Not Flags The `NOT` instruction flips all the bits in the operand, from 0 to 1 or from 1 to 0. That's as simple as could be, but `NOT` nonetheless @@ -586,7 +586,7 @@ and which flags are set, for example—can be critical when you're trying to optimize a code sequence and you're running out of registers, or when you're trying to minimize branching. -#### Incrementing with and without Carry {#Heading10} +#### Incrementing with and without Carry Another case in which there are two slightly different ways to perform a task involves adding 1 to an operand. You can do this with `INC`, as diff --git a/chapter-08.md b/chapter-08.md index 25e2e2a..1e30761 100644 --- a/chapter-08.md +++ b/chapter-08.md @@ -11,9 +11,9 @@ pages: 149-166 --- ## Chapter 8\ - Speeding Up C with Assembly Language {#Heading1} + Speeding Up C with Assembly Language -### Jumping Languages When You Know It'll Help {#Heading2} +### Jumping Languages When You Know It'll Help When I was a senior in high school, a pop song called "Seasons in the Sun," sung by one Terry Jacks, soared up the pop charts and spent, as @@ -59,7 +59,7 @@ language output and tweaking it. Apropos of which, when was the last time you heard of Terry Jacks? -#### Billy, Don't Be a Compiler {#Heading3} +#### Billy, Don't Be a Compiler The key to optimizing C programs with assembly language is, as always, writing good assembly language code, but with an added twist. Rule 1 @@ -99,7 +99,7 @@ assembly language optimization. > future changes and debugging more difficult, slowing you down and > limiting your options. -### Don't Call Your Functions on Me, Baby {#Heading4} +### Don't Call Your Functions on Me, Baby In order to think differently from a compiler, you must understand both what compilers and C programmers tend to do and how that differs from @@ -130,7 +130,7 @@ extra cycles they take don't affect performance, then the code they're in probably isn't critical, and perhaps you've chosen to convert too much code to assembly, eh? -### Stack Frames Slow So Much {#Heading5} +### Stack Frames Slow So Much C compilers work within the stack frame model, whereby variables reside in a block of stack memory and are accessed via offsets from BP. @@ -149,7 +149,7 @@ sometimes useful indeed. That doesn't mean you shouldn't use stack frames, which are useful and often necessary. Just don't fall victim to their undeniable charms. -### Torn Between Two Segments {#Heading6} +### Torn Between Two Segments C compilers are not terrific at handling segments. Some compilers can efficiently handle a single far pointer used in a loop by leaving ES set @@ -166,7 +166,7 @@ address to be reloaded each time either pointer is used. In assembly language you have full control over segments. Use it, and, if necessary, reorganize your code to minimize segment loading. -#### Why Speeding Up Is Hard to Do {#Heading7} +#### Why Speeding Up Is Hard to Do You might think that the most obvious advantage assembly language has over C is that it allows the use of all forms of instructions and all @@ -219,7 +219,7 @@ jz Match It's a simple example—but, I hope, a convincing one. Stretch your brain when you optimize. -### Taking It to the Limit {#Heading8} +### Taking It to the Limit The ultimate in assembly language optimization comes when you change the rules; that is, when you reorganize the entire program to allow the use @@ -260,7 +260,7 @@ code into optimized assembly language: That said, let me show some of these precepts in action. -#### A C-to-Assembly Case Study {#Heading9} +#### A C-to-Assembly Case Study Listing 8.1 is the sample C application I'm going to use to examine optimization in action. Listing 8.1 isn't really complete—it doesn't diff --git a/chapter-09.md b/chapter-09.md index dcb80d9..933d1a2 100644 --- a/chapter-09.md +++ b/chapter-09.md @@ -11,9 +11,9 @@ pages: 167-188 --- ## Chapter 9\ - Hints My Readers Gave Me {#Heading1} + Hints My Readers Gave Me -### Optimization Odds and Ends from the Field {#Heading2} +### Optimization Odds and Ends from the Field Back in high school, I took a pre-calculus class from Mr. Bourgeis, whose most notable characteristics were incessant pacing and truly @@ -76,7 +76,7 @@ and many readers have sent me a slew of those over the years. So in this chapter, I think I'll return the favor by devoting a chapter to reader feedback. -#### Another Look at LEA {#Heading3} +#### Another Look at LEA Several people have pointed out that while `LEA` is great for performing certain additions (see Chapter 6), it isn't a perfect @@ -136,7 +136,7 @@ on what you're trying to do. But there sure are a lot of interesting options, aren't there? -#### The Kennedy Portfolio {#Heading4} +#### The Kennedy Portfolio Reader John Kennedy regularly passes along intriguing assembly programming tricks, many of which I've never seen mentioned anywhere @@ -247,7 +247,7 @@ SHL AX,2 ;*64 ADD AX,BX ;*80 ``` -#### Speeding Up Multiplication {#Heading5} +#### Speeding Up Multiplication That brings us to multiplication, one of the slowest of x86 operations and one that allows for considerable optimization. One way to speed up @@ -302,7 +302,7 @@ that's always in the range of, say, 2 to 10; because the scale value will always be small and the array elements may have any value, the scale value is the logical choice for the multiplier. -#### Optimizing Optimized Searching {#Heading6} +#### Optimizing Optimized Searching Rob Williams writes with a wonderful optimization to the `REPNZ SCASB`-based optimized searching routine I discussed in Chapter 5. As a @@ -617,7 +617,7 @@ the difference lies not in elbow grease or cycle counting but in the organic integrating optimizer technology we all carry around in our heads. -#### Short Sorts {#Heading7} +#### Short Sorts David Stafford (recently of Borland and Borland Japan) who happens to be one of the best assembly language programmers I've ever met, has written @@ -666,7 +666,7 @@ _sort: pop dx ;get return address (entry point) end ``` -#### Full 32-Bit Division {#Heading8} +#### Full 32-Bit Division One of the most annoying limitations of the x86 is that while the dividend operand to the `DIV` instruction can be 32 bits in size, both @@ -802,7 +802,7 @@ main() { } ``` -#### Sweet Spot Revisited {#Heading9} +#### Sweet Spot Revisited Way back in Volume 1, Number 1 of *PC TECHNIQUES*, (April/May 1990) I wrote the very first of that magazine's HAX (\#1), which extolled the @@ -844,7 +844,7 @@ a large one) by several K—not bad, when you consider that the "sweet spot" optimization is essentially free, with no code reorganization, change in logic, or heavy thinking involved. -#### Hard-Core Cycle Counting {#Heading10} +#### Hard-Core Cycle Counting Next, we come to an item that cycle counters will love, especially since it involves apparently incorrect documentation on Intel's part. @@ -876,7 +876,7 @@ No great lesson here, just a caution to be leery of multibit `RCR` and `RCL` when performance matters—and to take cycle-time documentation with a grain of salt. -#### Hardwired Far Jumps {#Heading11} +#### Hardwired Far Jumps Did you ever wonder how to code a far jump to an absolute address in assembly language? Probably not, but if you ever do, you're going to be @@ -949,7 +949,7 @@ If the obvious doesn't work (and it usually doesn't), just try everything you can think of, no matter how ridiculous, until you find something that does—a rule with plenty of history on its side. -#### Setting 32-Bit Registers: Time versus Space {#Heading12} +#### Setting 32-Bit Registers: Time versus Space To finish up this chapter, consider these two items. First, in 32-bit protected mode, diff --git a/chapter-10.md b/chapter-10.md index da88efb..88854c7 100644 --- a/chapter-10.md +++ b/chapter-10.md @@ -11,9 +11,9 @@ pages: 190-203 --- ## Chapter 10\ - Patient Coding, Faster Code {#Heading1} + Patient Coding, Faster Code -### How Working Quickly Can Bring Execution to a Crawl {#Heading2} +### How Working Quickly Can Bring Execution to a Crawl My grandfather does *The New York Times* crossword puzzle every Sunday. In ink. With nary a blemish. @@ -84,7 +84,7 @@ will be much better—and you'll never even have the chance to decide whether they're better or not if you take the first thing that comes into your head and run with it. -#### The Case for Delayed Gratification {#Heading3} +#### The Case for Delayed Gratification Once upon a time, I set out to read *Algorithms*, by Robert Sedgewick (Addison-Wesley), which turned out to be a wonderful, stimulating, and @@ -110,7 +110,7 @@ The problem at hand, then, is simply this: Find the largest integer value that evenly divides two arbitrary positive integers. That's all there is to it. So warm up your pattern matchers...and go! -### The Brute-Force Syndrome {#Heading4} +### The Brute-Force Syndrome I have a funny feeling that you'd already figured out how to find the GCD before I even said "go." That's what I did when reading @@ -217,7 +217,7 @@ unsigned int gcd(unsigned int int1, unsigned int int2) { } ``` -#### Wasted Breakthroughs {#Heading5} +#### Wasted Breakthroughs Sedgewick's first solution to the GCD problem was pretty much the one I came up with. He then pointed out that the GCD of iL and iS is the same @@ -364,7 +364,7 @@ your ears does its best work not at the implementation stage, but at the very beginning, when you try to imagine how what you want to do and what a computer is capable of doing can best be brought together. -### Recursion {#Heading6} +### Recursion Euclid's algorithm lends itself to recursion beautifully, so much so that an implementation like Listing 10.3 comes almost without thought. @@ -417,7 +417,7 @@ unsigned int gcd(unsigned int int1, unsigned int int2) { } ``` -#### Patient Optimization {#Heading7} +#### Patient Optimization At long last, we're ready to optimize GCD determination in the classic sense. Table 10.1 shows the performance of Listing 10.4 with and without diff --git a/chapter-11.md b/chapter-11.md index 21d6d64..ffc8fa6 100644 --- a/chapter-11.md +++ b/chapter-11.md @@ -11,9 +11,9 @@ pages: 205-231 --- ## Chapter 11\ - Pushing the 286 and 386 {#Heading1} + Pushing the 286 and 386 -### New Registers, New Instructions, New Timings, New Complications {#Heading2} +### New Registers, New Instructions, New Timings, New Complications This chapter, adapted from my earlier book *Zen of Assembly Language* (1989; now out of print), provides an overview of the 286 and 386, often @@ -40,7 +40,7 @@ mainstream of computing, this chapter is a useful mix of history lesson, x86 overview, and details on two workhorse processors that are still in wide use. -#### Family Matters {#Heading3} +#### Family Matters While the x86 family is a large one, only a few members of the family—which includes the 8088, 8086, 80188, 80186, 286, 386SX, 386DX, @@ -80,7 +80,7 @@ its lifespan, and it is in even wider use than the 286. The future clearly belongs to the 486 and Pentium, but the 286 and 386 are still very much a part of the present-day landscape. -#### Crossing the Gulf to the 286 and the 386 {#Heading4} +#### Crossing the Gulf to the 286 and the 386 Apart from vastly improved performance, the biggest difference between the 8088 and the 286 and 386 (as well as the later Intel CPUs) is that @@ -135,7 +135,7 @@ optimization strategies discussed in this book still hold true in protected mode; it's just issues specific to protected mode or a particular operating system that we won't discuss. -#### In the Lair of the Cycle-Eaters, Part II {#Heading5} +#### In the Lair of the Cycle-Eaters, Part II Under the programming interface, the 286 and 386 differ considerably from the 8088. Nonetheless, with one exception and one addition, the @@ -447,7 +447,7 @@ can literally double the performance of certain code running on the 286. Even if it doesn't double performance, word alignment usually helps and never hurts. -#### Code Alignment {#Heading8} +#### Code Alignment Lack of word alignment can also interfere with instruction fetching on the 286, although not to the extent that it interferes with access to @@ -698,7 +698,7 @@ What can we do about this new, more virulent form of the display adapter cycle-eater? The workaround is the same as it was on the PC: Access display memory as little as you possibly can. -#### New Instructions and Features: The 286 {#Heading13} +#### New Instructions and Features: The 286 The 286 and 386 offer a number of new instructions. The 286 has a relatively small number of instructions that the 8088 lacks, while the @@ -731,7 +731,7 @@ For another, the 286 allows all shifts and rotates to be performed for not just 1 bit or the number of bits specified by CL, but for *any* constant number of bits. -#### New Instructions and Features: The 386 {#Heading14} +#### New Instructions and Features: The 386 The 386 is somewhat more complex than the 286 regarding new features. Once again, we won't discuss protected mode, which on the 386 comes with @@ -941,7 +941,7 @@ the like. The more things change, the more they remain the same.... -#### POPF and the 286 {#Heading17} +#### POPF and the 286 We've one final 286-related item to discuss: the hardware malfunction of `POPF` under certain circumstances on the 286. diff --git a/chapter-12.md b/chapter-12.md index d54e61d..82ecd75 100644 --- a/chapter-12.md +++ b/chapter-12.md @@ -11,9 +11,9 @@ pages: 233-246 --- ## Chapter 12\ - Pushing the 486 {#Heading1} + Pushing the 486 -### It's Not Just a Bigger 386 {#Heading2} +### It's Not Just a Bigger 386 So this traveling salesman is walking down a road, and he sees a group of men digging a ditch with their bare hands. "Whoa, there!" he says. @@ -45,7 +45,7 @@ register-to-register `MOV`s, Dorothy was heard to exclaim (before she sank out of sight in a swirl of hopelessly mixed metaphors), "I don't think we're in Kansas anymore, Toto." -#### Enter the 486 {#Heading3} +#### Enter the 486 No chip that is a direct, fully compatible descendant of the 8088, 286, and 386 could ever be called a RISC chip, but the 486 certainly contains @@ -72,7 +72,7 @@ Intel; "8086 Optimization: Aim Down the Middle and Pray," in the March, 1991 *Dr. Dobb's Journal*; and "Peak Performance: On to the 486," in the November, 1990 *Programmer's Journal.* -### Rules to Optimize By {#Heading4} +### Rules to Optimize By In Appendix G of the *i486 Microprocessor Programmer*'*s* *Reference Manual*, Intel lists a number of optimization techniques for the 486. @@ -102,7 +102,7 @@ the rules, documented and undocumented, that go into calculating actual execution times—and uncovering some of those rules is exactly what this chapter is about. -#### The Hazards of Indexed Addressing {#Heading5} +#### The Hazards of Indexed Addressing Rule \#1: Avoid indexed addressing (that is, try not to use either two registers or scaled addressing to point to memory). @@ -176,7 +176,7 @@ makes the *whole loop* more than 14 percent faster. In a key loop on the 486, 1 cycle can indeed matter. -#### Calculate Memory Pointers Ahead of Time {#Heading6} +#### Calculate Memory Pointers Ahead of Time Rule \#2: Don't use a register as a memory pointer during the next two cycles after loading it. @@ -295,7 +295,7 @@ rearrangement of 486 code. ![**Figure 12.2**  *Two-cycle-ahead address pipelining.*](images/12-02.jpg) -### Caveat Programmor {#Heading7} +### Caveat Programmor A caution: I'm quite certain that the 2-cycle-ahead addressing pipeline interruption penalty I've described exists in the two 486s I've tested. @@ -319,7 +319,7 @@ undocumented optimizations, please write and let me know. And, of course, if anyone from Intel is reading this and wants to give us the gospel truth, please do! -#### Stack Addressing and Address Pipelining {#Heading8} +#### Stack Addressing and Address Pipelining Rule \#2A: Rule \#2 sometimes, but not always, applies to the stack pointer when it is implicitly used to point to memory. @@ -396,7 +396,7 @@ from the stack pointer should ideally be done at least two cycles before `PUSH`, `POP`, `RET`, or any other instruction that uses the stack pointer to address memory. -#### Problems with Byte Registers {#Heading9} +#### Problems with Byte Registers There are two ways to lose cycles by using byte registers, and neither of them is documented by Intel, so far as I know. Let's start with the @@ -479,7 +479,7 @@ you're a diehard ASMhead who does this stuff for fun. Just learn enough to be able to speed up the key portions of your programs, and spend the rest of your time on a fast design and overall implementation. -#### More Fun with Byte Registers {#Heading10} +#### More Fun with Byte Registers Rule \#4: Don't load *any* byte register exactly 2 cycles before using *any* register to address memory. @@ -552,7 +552,7 @@ can affect. > to address memory, and try not to load a register either one or two > cycles before using it to address memory, and you'll be fine. -#### Timing Your Own 486 Code {#Heading11} +#### Timing Your Own 486 Code In case you want to do some 486 performance analysis of your own, let me show you how I arrived at one of the above conclusions; at the same @@ -624,7 +624,7 @@ bytes. > Whenever you see non-integral timing results of this sort, it's a good > bet that the test code or data isn't cached. -### The Story Continues {#Heading12} +### The Story Continues There's certainly plenty more 486 lore to explore, including the 486's unique prefetch queue, more optimization rules, branching optimizations, diff --git a/chapter-13.md b/chapter-13.md index 903d64d..92bc43c 100644 --- a/chapter-13.md +++ b/chapter-13.md @@ -11,9 +11,9 @@ pages: 248-258 --- ## Chapter 13\ - Aiming the 486 {#Heading1} + Aiming the 486 -### Pipelines and Other Hazards of the High End {#Heading2} +### Pipelines and Other Hazards of the High End It's a sad but true fact that 84 percent of American schoolchildren are ignorant of 92 percent of American history. Not my daughter, though. We @@ -58,7 +58,7 @@ For example, consider how Terje Mathisen doubled the speed of his word-counting program on a 486 simply by shuffling a couple of instructions. -#### 486 Pipeline Optimization {#Heading3} +#### 486 Pipeline Optimization I've mentioned Terje Mathisen in my writings before. Terje is an assembly language programmer extraordinaire, and author of the @@ -167,7 +167,7 @@ engine can process more than 16 million characters *per second* on a Clever 486 optimization can pay off big. QED. -### BSWAP: More Useful Than You Might Think {#Heading4} +### BSWAP: More Useful Than You Might Think There are only 3 non-system instructions unique to the 486. None is earthshaking, but they have their uses. Consider `BSWAP`. `BSWAP` does @@ -268,7 +268,7 @@ looptop: jnz looptop ``` -### Pushing and Popping Memory {#Heading5} +### Pushing and Popping Memory Pushing or popping a memory location, as in `PUSH WORD PTR [BX]` or `POP [MemVar]`, is a compact, easy way to get a value onto or off of @@ -315,7 +315,7 @@ well as `XLAT`, `LOOP`, and, of course, `PUSH *mem*` and `POP > pipeline efficiency, as is the case with Terje's optimization described > earlier in this chapter. -### Optimal 1-Bit Shifts and Rotates {#Heading6} +### Optimal 1-Bit Shifts and Rotates On a 486, the n-bit forms of the shift and rotate instructions—as in `ROR AX,2` and `SHL BX,9`—are 2-cycle instructions, but the 1-bit @@ -357,7 +357,7 @@ critical cycles—and Lord knows that if you're optimizing for the unoptimized code on a 486—you almost certainly need all the speed you can get. -### 32-Bit Addressing Modes {#Heading7} +### 32-Bit Addressing Modes The 386 and 486 both support 32-bit addressing modes, in which any register may serve as the base memory addressing register, and almost diff --git a/chapter-14.md b/chapter-14.md index 4e27889..35bc9b6 100644 --- a/chapter-14.md +++ b/chapter-14.md @@ -11,9 +11,9 @@ pages: 260-277 --- ## Chapter 14\ - Boyer-Moore String Searching {#Heading1} + Boyer-Moore String Searching -### Optimizing a Pretty Optimum Search Algorithm {#Heading2} +### Optimizing a Pretty Optimum Search Algorithm When you seem to be stumped, stop for a minute and *think.* All the information you need may be right in front of your nose if you just look @@ -61,7 +61,7 @@ said, "Hey! Did you guys put in a new floor?" As I said, sometimes everything you need to know is right in front of your nose. Which brings us to Boyer-Moore string searching. -### String Searching Refresher {#Heading3} +### String Searching Refresher I've discussed string searching earlier in this book, in Chapters 5 and 9. You may want to refer back to these chapters for some background on @@ -132,7 +132,7 @@ matches. Can we? Actually, yes, we can. -### The Boyer-Moore Algorithm {#Heading4} +### The Boyer-Moore Algorithm All our *a priori* knowledge of string searching is stated above, but there's another sort of knowledge—knowledge that's generated @@ -228,7 +228,7 @@ is to it! ![**Figure 14.2**  *Mismatch on third character checked.*](images/14-02.jpg) -### Boyer-Moore: The Good and the Bad {#Heading5} +### Boyer-Moore: The Good and the Bad The worst case for this version of Boyer-Moore is that the pattern mismatches on the leftmost character—the last character compared—every @@ -627,7 +627,7 @@ thereby getting the best of both worlds.) Know your data and use your smarts. Don't stop thinking just because you're implementing a big-name algorithm; you know more than it does. -### Further Optimization of Boyer-Moore {#Heading6} +### Further Optimization of Boyer-Moore We can do substantially better yet than Listing 14.3 if we're willing to accept tighter limits on the data. Limiting the length of the @@ -797,7 +797,7 @@ all but the first search when repeatedly searching for a particular pattern, by building the skip table externally and passing a pointer to it as a parameter. -### Know What You Know {#Heading7} +### Know What You Know Here we've turned up our nose at a repeated string instruction, we've gone against the grain by comparing backward, and yet we've speeded up diff --git a/chapter-15.md b/chapter-15.md index 7fdadef..1e1078a 100644 --- a/chapter-15.md +++ b/chapter-15.md @@ -11,9 +11,9 @@ pages: 279-293 --- ## Chapter 15\ - Linked Lists and plain Unintended Challenges {#Heading1} + Linked Lists and plain Unintended Challenges -### Unfamiliar Problems with Familiar Data Structures {#Heading2} +### Unfamiliar Problems with Familiar Data Structures After 21 years, this story still makes me wince. Oh, the humiliations I suffer for your enlightenment.... @@ -76,7 +76,7 @@ lifetime at this stuff and happens to be a genius? Maybe you can—but I sure can't. For example, consider the evolution of my understanding of linked lists. -### Linked Lists {#Heading3} +### Linked Lists Linked lists are data structures composed of discrete elements, or nodes, joined together with links. In C, the links are typically @@ -230,7 +230,7 @@ nodes, and in fact in all link manipulation code. It's easy to end up working with either pointers to pointers or lots of special-case code, and while those approaches work, they're inelegant and inefficient. -### Dummies and Sentinels {#Heading4} +### Dummies and Sentinels A far better approach is to use a *dummy node* for the head of the list, as shown in Figure 15.2. I invented this one for myself the next time I @@ -335,7 +335,7 @@ struct LinkNode *FindNodeBeforeValueNotLess( ![**Figure 15.4**  *List terminated by a sentinel.*](images/15-04.jpg) -### Circular Lists {#Heading5} +### Circular Lists One minor but elegant refinement yet remains: Use a single node as both the head *and* the tail of the list. We can do this by connecting the @@ -597,7 +597,7 @@ void main() } ``` -### Hi/Lo in 24 Bytes {#Heading6} +### Hi/Lo in 24 Bytes In one of my *PC TECHNIQUES* "Pushing the Envelope" columns, I passed along one of David Stafford's fiendish programming puzzles: Write a diff --git a/chapter-16.md b/chapter-16.md index b8979ba..7d077ac 100644 --- a/chapter-16.md +++ b/chapter-16.md @@ -11,9 +11,9 @@ pages: 295-319 --- ## Chapter 16\ - There Ain't No Such Thing as the Fastest Code {#Heading1} + There Ain't No Such Thing as the Fastest Code -### Lessons Learned in the Pursuit of the Ultimate Word Counter {#Heading2} +### Lessons Learned in the Pursuit of the Ultimate Word Counter I remember reading an overview of C++ development tools for Windows in a past issue of *PC Week*. In the lower left corner was the familiar box @@ -54,7 +54,7 @@ particularly attractive keys. We are talking about people who are focusing on means, and have forgotten about ends. We are talking about people with no programming souls. -### Counting Words in a Hurry {#Heading3} +### Counting Words in a Hurry What are we to make of this? At the very least, we can safely guess that very few corporate buyers ever enter optimization contests. Most of my @@ -331,7 +331,7 @@ _ScanBuffer endp end ``` -#### Which Way to Go from Here? {#Heading4} +#### Which Way to Go from Here? We could rearrange the tests in light of the nature of the data being scanned; for example, we could perform the tests more efficiently by @@ -487,7 +487,7 @@ difficult. > ![](images/i.jpg) > Exhaust all other optimizations before unrolling loops. -### Challenges and Hazards {#Heading5} +### Challenges and Hazards The challenge I put to the readers of *PC TECHNIQUES* was to write a faster module to replace Listing 16.4. The author of the code that @@ -520,7 +520,7 @@ code is the fastest possible is rollescating on a tightrope in a hurricane; you're due for a fall, if you catch my drift. Case in point: Terje Mathisen's word-counting program. -#### Blinding Yourself to a Better Approach {#Heading6} +#### Blinding Yourself to a Better Approach Not so long ago, Terje Mathisen, who I introduced earlier in this book, wrote a very fast word-counting program, and posted it on Bix. When I @@ -561,7 +561,7 @@ enough, by good fortune, to speed up the whole program by 5 percent. this case, though, the code was specific to the 386. In case you're curious, both forms take 2 cycles on the 486; quite a lot faster, eh?) -#### Watch Out for Luggable Assumptions! {#Heading7} +#### Watch Out for Luggable Assumptions! The first lesson to be learned here is not to lug assumptions that may no longer be valid from the 8088/286 world into the wonderful new world @@ -613,7 +613,7 @@ if you're interested in really fast assembly code. I wouldn't call it the *fastest* word-counting code, though, because I would of course never be so foolish as to call *anything* the fastest. -### The Astonishment of Right-Brain Optimization {#Heading8} +### The Astonishment of Right-Brain Optimization As it happened, the challenge I issued to my *PC TECHNIQUES* readers was a smashing success, with dozens of good entries. I certainly enjoyed it, @@ -847,7 +847,7 @@ jumping. end ``` -### Levels of Optimization {#Heading9} +### Levels of Optimization Three levels of optimization were evident in the word-counting entries I received in response to my challenge. I'd briefly describe them as @@ -865,7 +865,7 @@ the code, the greater the chance for obscure bugs.) > like buying Telly Savalas a comb; it's not going to do any harm, but > it's nonetheless a waste of time. -#### Optimization Level 1: Good Code {#Heading10} +#### Optimization Level 1: Good Code The first level of optimization involves fine-tuning and clever use of the instruction set. The basic framework is still the same as my code @@ -1038,7 +1038,7 @@ _ScanBuffer endp end ``` -### Level 2: A New Perspective {#Heading11} +### Level 2: A New Perspective The second level of optimization is one of breaking out of the mode of thinking established by my original code. Some entrants clearly did @@ -1117,7 +1117,7 @@ John's approach makes it clear that word-counting is nothing more than a fairly simple state machine. The interesting part, of course, is building the fastest state machine. -#### Level 3: Breakthrough {#Heading12} +#### Level 3: Breakthrough The boundaries between the levels of optimization are not sharply defined. In a sense, level 3 optimization is just like levels 1 and 2, @@ -1233,7 +1233,7 @@ cycles/byte. Enough said, I trust. -#### Enough Word Counting Already! {#Heading13} +#### Enough Word Counting Already! Before I finish up this chapter, I'd like to mention that Terje Mathisen's WC word-counting program, which I've mentioned previously and diff --git a/chapter-17.md b/chapter-17.md index 04615ca..8671ac1 100644 --- a/chapter-17.md +++ b/chapter-17.md @@ -11,9 +11,9 @@ pages: 322-346 --- ## Chapter 17\ - The Game of Life {#Heading1} + The Game of Life -### The Triumph of Algorithmic Optimization in a Cellular Automata Game {#Heading2} +### The Triumph of Algorithmic Optimization in a Cellular Automata Game I've spent a lot of my life discussing assembly language optimization, which I consider to be an important and underappreciated topic. However, @@ -40,7 +40,7 @@ we will find is that it's possible to get a 50-times speed-up without using *one byte of assembly!* It's all a matter of perspective—how you look at your code and data. -### Conway's Game {#Heading3} +### Conway's Game The program that we're going to optimize is Conway's famous Game of Life, long-ago favorite of the hackers at MIT's AI Lab. If you've never @@ -64,7 +64,7 @@ speed-up range. Then in the next chapter, I'll show you how several programmers *really* floored it in taking me up on my second Optimization Challenge, which involved the Game of Life. -#### The Rules of the Game {#Heading4} +#### The Rules of the Game The Game of Life is ridiculously simple. There is a cellmap, consisting of a rectangular matrix of cells, each of which may initially be either @@ -359,7 +359,7 @@ void show_text(int x, int y, char *text) } ``` -### Where Does the Time Go? {#Heading5} +### Where Does the Time Go? How slow is Listing 17.1? Table 17.1 shows that even on a 486, Listing 17.1 does fewer than three 96x96 generations per second. (The times in @@ -415,7 +415,7 @@ at for possible optimizations are `cell_state()` and > knowing the nature of your data, and is a potent optimization technique > that will be extremely useful a little later in this chapter. -### The Hazards and Advantages of Abstraction {#Heading6} +### The Hazards and Advantages of Abstraction How can we speed up `cell_state()` and `next_generation()`? I'll tell you how *not* to do it: By writing those member functions in @@ -685,7 +685,7 @@ assembly? Not hardly. -### Heavy-Duty C++ Optimization {#Heading7} +### Heavy-Duty C++ Optimization Before we get to assembly, we still have to perform C++ optimization, then see if we can find an alternative approach that better fits the @@ -826,7 +826,7 @@ How is this possible? Here are some hints: bit-per-pixel. * Cells change state relatively infrequently. -### Bringing In the Right Brain {#Heading8} +### Bringing In the Right Brain In the previous section, we saw how a C++ program could be sped up about eight times simply by rearranging the data and code in straightforward @@ -843,7 +843,7 @@ encourage you to saturate your brain with everything you know about any particular optimization problem, then make space for your right brain to solve the problem. -#### Re-Examining the Task {#Heading9} +#### Re-Examining the Task Earlier in this chapter, we looked at a straightforward Game of Life implementation, then increased performance considerably by making the @@ -907,7 +907,7 @@ only one-tenth that of the original approach! ![**Figure 17.3**  *New cell format.*](images/17-03.jpg) -#### Acting on What We Know {#Heading10} +#### Acting on What We Know Once we've changed the cellmap format to store neighbor counts as well as states, with a byte for each cell, we can get another performance @@ -1254,7 +1254,7 @@ No doubt we could get another two to five times improvement with good assembly code—but that's dwarfed by a 30-times improvement, so optimization at a conceptual level *must* come first. -#### The Challenge That Ate My Life {#Heading11} +#### The Challenge That Ate My Life The most recent optimization challenge I laid my community of readers was to write the fastest possible Game of Life generation engine. By diff --git a/chapter-18.md b/chapter-18.md index 1a06065..5491ebb 100644 --- a/chapter-18.md +++ b/chapter-18.md @@ -11,9 +11,9 @@ pages: 347-367 --- ## Chapter 18\ - It's a plain Wonderful Life {#Heading1} + It's a plain Wonderful Life -### Optimization beyond the Pale {#Heading2} +### Optimization beyond the Pale When I was in high school, my gym teacher had us run a race around the soccer field, or rather, around a course marked with cones that roughly @@ -51,7 +51,7 @@ programming. > will often be unable to change the specifications for the software you > implement. -### Breaking the Rules {#Heading3} +### Breaking the Rules The other reason for the anecdote has to do with the way my second Optimization Challenge worked itself out. If you'll recall from the last @@ -119,7 +119,7 @@ specific "David Stafford" or "Peter Klerings.") Onward to the code. -### Table-Driven Magic {#Heading4} +### Table-Driven Magic David Stafford won my first Optimization Challenge by means of a huge look-up table and an incredible state machine driven by that table. The @@ -859,7 +859,7 @@ extern unsigned short far ChangeList1[]; #define WRAPDOWN (UP * (HEIGHT - 1)) ``` -### Keeping Track of Change with a Change List {#Heading5} +### Keeping Track of Change with a Change List In my earlier optimizations to the Game of Life, described in the last chapter, I noted that most cells in a Life cellmap are dead, and in most @@ -966,7 +966,7 @@ Segment usage in David's assembly code is summarized in Listing 18.6. FS : Video segment GS : Unused -#### A Layperson's Overview of QLIFE {#Heading6} +#### A Layperson's Overview of QLIFE Most likely, you're scratching your head right now in bemusement. I don't blame you; I felt the same way myself at first. It's actually diff --git a/chapter-19.md b/chapter-19.md index 15b72f4..a53f93b 100644 --- a/chapter-19.md +++ b/chapter-19.md @@ -11,9 +11,9 @@ pages: 369-379 --- ## Chapter 19\ - Pentium: Not the Same Old Song {#Heading1} + Pentium: Not the Same Old Song -### Learning a Whole Different Set of Optimization Rules {#Heading2} +### Learning a Whole Different Set of Optimization Rules I can still remember the day I did my first 8088 programming. I had just moved over from the distantly related Z80, so the 8088 wasn't totally @@ -45,7 +45,7 @@ that made cycle counting more meaningful than ever before, and careful code massaging sometimes yielded startling results. Nonetheless, the 486 was still too simple to mark a return to the golden age of optimization. -### The Return of Optimization as Art {#Heading3} +### The Return of Optimization as Art Then the Pentium came around, and filled our code with optimization hazards, and life was good again. The Pentium has two execution @@ -90,7 +90,7 @@ pitch Preparation H. I can hardly wait. Gimme a "P".... -### The Pentium: An Overview {#Heading4} +### The Pentium: An Overview Architecturally, the Pentium is vastly different in many ways from the 486, but most of those differences are transparent to programmers. After @@ -139,7 +139,7 @@ to 128 bytes. In conjunction with the branch prediction feature branches, this larger prefetch queue means that the Pentium's two pipes should be better fed than those of any previous x86 processor. -#### Crossing Cache Lines {#Heading5} +#### Crossing Cache Lines There are three other characteristics of the Pentium that make for a healthy supply of instruction bytes. One is that the Pentium can @@ -189,7 +189,7 @@ Architecture and Programming Manual* (ISBN 1-55512-195-0; Intel order number 241430-001), and the article "Optimizing Pentium Code" by Mike Schmidt, in *Dr. Dobb's Journal* for January 1994. -#### Cache Organization {#Heading6} +#### Cache Organization There are two other interesting changes in the Pentium's cache organization. First, the cache is two-way set-associative, whereas the @@ -243,7 +243,7 @@ improvement. Clearly, avoiding AGIs becomes a much more challenging and rewarding game in a superscalar world, one to which I'll return in the next chapter. -### Faster Addressing and More {#Heading7} +### Faster Addressing and More I'll spend the rest of this chapter covering a variety of Pentium optimization tips. For starters, effective address calculations (that @@ -346,7 +346,7 @@ Pentium may have to shut down the V-pipe for a cycle to avoid potential dependencies on the result of the `AND` or `OR`. `TEST` suffers from no such potential dependencies. -### Branch Prediction {#Heading8} +### Branch Prediction One brand-spanking-new feature of the Pentium is *branch prediction*, whereby the Pentium tries to guess, based on past history, which way @@ -406,7 +406,7 @@ on any given iteration. > to fall through branches if possible, and try to be consistent in your > branching if not. -### Miscellaneous Pentium Topics {#Heading9} +### Miscellaneous Pentium Topics The Pentium has all the instructions of the 486, plus a few new ones. One much-needed instruction that has finally made it into the @@ -418,7 +418,7 @@ to me to be a particularly useful instruction, but I'm sure Intel wouldn't have added it without a reason; if you know of a use for it, please pass it along to me. -#### 486 versus Pentium Optimization {#Heading10} +#### 486 versus Pentium Optimization Many Pentium optimizations help, or at least don't hurt, on the 486. Many, but not all—and many *do* hurt on the 386. As I discuss various @@ -438,7 +438,7 @@ absolutely the best possible performance for your DOS and Windows apps on the fastest hardware, Pentium optimization can make your code *scream*. -#### Going Superscalar {#Heading11} +#### Going Superscalar In the next chapter, we'll look into the single biggest element of Pentium performance, cranking up the Pentium's second execution pipe. diff --git a/chapter-20.md b/chapter-20.md index 2be655e..cc5c3aa 100644 --- a/chapter-20.md +++ b/chapter-20.md @@ -11,9 +11,9 @@ pages: 381-396 --- ## Chapter 20\ - Pentium Rules {#Heading1} + Pentium Rules -### How Your Carbon-Based Optimizer Can Put the "Super" in Superscalar {#Heading2} +### How Your Carbon-Based Optimizer Can Put the "Super" in Superscalar At the 1983 West Coast Computer Faire, my friend Dan Illowsky, Andy Greenberg (co-author of Wizardry, at that time the best-selling computer @@ -71,7 +71,7 @@ that the `FXCH` instruction, which is largely free on the Pentium, is expensive on the 486.) So discard your x86 preconceptions as we delve into superscalar optimization for this one-of-a-kind processor. -### An Instruction in Every Pipe {#Heading3} +### An Instruction in Every Pipe In the last chapter, we took a quick tour of the Pentium's architecture, and started to look into the Pentium's optimization rules. Now we're @@ -139,7 +139,7 @@ practice, this is not too difficult. The only hard part is keeping in mind the long list of rules governing instruction pairing. The place to begin is with the set of instructions that can go through the V-pipe. -### V-Pipe-Capable Instructions {#Heading4} +### V-Pipe-Capable Instructions Any instruction can go through the U-pipe, and, for practical purposes, the U-pipe is always executing instructions. (The exceptions are when @@ -327,7 +327,7 @@ sequence can be reduced to 1.5 cycles, but it is *14* bytes long. > performance and ignore the size, but on a program-wide basis, the size > bears watching. -### Lockstep Execution {#Heading5} +### Lockstep Execution You may wonder why anyone would bother breaking `ADD [MemVar],EAX` into three instructions, given that this instruction can go through @@ -470,7 +470,7 @@ one-cycle instructions, mixed together so that at least two operations are in progress at once. It's not the easiest code to read or write, but it's the only way to get both pipes running at capacity. -### Superscalar Notes {#Heading6} +### Superscalar Notes You may well ask why it's necessary to interleave operations, as is done in Figure 20.7. It seems simpler just to turn @@ -505,7 +505,7 @@ hazard known as *register contention*. I'll return to the subject of register contention in the next chapter; in the remainder of this chapter I'd like to cover a few short items about superscalar execution. -#### Register Starvation {#Heading7} +#### Register Starvation The above examples should make it pretty clear that effective superscalar programming puts a lot of strain on the Pentium's relatively diff --git a/chapter-21.md b/chapter-21.md index 6bcd024..c59f28d 100644 --- a/chapter-21.md +++ b/chapter-21.md @@ -11,9 +11,9 @@ pages: 397-411 --- ## Chapter 21\ - Unleashing the Pentium's V-Pipe {#Heading1} + Unleashing the Pentium's V-Pipe -### Focusing on Keeping Both Pentium Pipes Full {#Heading2} +### Focusing on Keeping Both Pentium Pipes Full The other day, my daughter suggested that we each draw the prettiest picture we could, then see whose was prettier. I won't comment on who @@ -52,7 +52,7 @@ written code from taking full advantage of the Pentium's two pipes, and can thereby keep your code from pushing the Pentium to maximum performance. -### Address Generation Interlocks {#Heading3} +### Address Generation Interlocks The Pentium is advertised as having a five-stage pipeline for each of its execution units. All this means is that at any given time, up to @@ -186,7 +186,7 @@ want to insert an instruction between the two `MOV`s—and, of course, this is yet another reason why you should always measure your code's actual performance. -### Register Contention {#Heading4} +### Register Contention Finally, we come to the last major component of superscalar optimization: register contention. The basic premise here is simple: You @@ -228,7 +228,7 @@ instructions comprising the above substitute for `MOVZX` should have at least one unrelated instruction between them when `SUB EAX,EAX` executes in the V-pipe. -#### Exceptions to Register Contention {#Heading5} +#### Exceptions to Register Contention Intel has special-cased some very useful exceptions to register contention. Happily, write-after-read operations do *not* cause @@ -285,7 +285,7 @@ U-pipe and 5 cycles in the V-pipe, and mispredicted calls and unconditional jumps take 3 cycles in either pipe. Note that `RET` can't pair. -### Who's in First? {#Heading6} +### Who's in First? One of the trickiest things about superscalar optimization is that a given instruction stream can execute at a different speed depending on @@ -321,7 +321,7 @@ usage, as discussed below. Shifts, rotates, `ADC, SBB`, and all other instructions not listed in Table 20.1 in the last chapter are likewise U-pipe markers. -### Pentium Optimization in Action {#Heading7} +### Pentium Optimization in Action Now, let's take a look at one of the simplest, tightest pieces of code imaginable, and see what our new Pentium perspective reveals. Listing @@ -646,7 +646,7 @@ measurement to check the efficacy of your optimizations, so reserve it for when you really, really need it—but when you need it, you need it *bad*. -#### A Quick Note on the 386 and 486 {#Heading8} +#### A Quick Note on the 386 and 486 I've mentioned that Pentium-optimized code does fine on the 486, but not always so well on the 386. On a 486, Listing 21.1 runs at 9 cycles per diff --git a/chapter-22.md b/chapter-22.md index 5e04a62..f1f1bd0 100644 --- a/chapter-22.md +++ b/chapter-22.md @@ -11,9 +11,9 @@ pages: 413-420 --- ## Chapter 22\ - Zenning and the Flexible Mind {#Heading1} + Zenning and the Flexible Mind -### Taking a Spin through What You've Learned {#Heading2} +### Taking a Spin through What You've Learned And so we come to the end of our journey; for now, at least. What follows is a modest bit of optimization, one which originally served to @@ -36,7 +36,7 @@ plain fun. Enjoy! -### Zenning {#Heading3} +### Zenning In Jeff Duntemann's excellent book *Borland Pascal From Square One* (Random House, 1993), there's a small assembly subroutine that's diff --git a/chapter-23.md b/chapter-23.md index 265f072..b52b693 100644 --- a/chapter-23.md +++ b/chapter-23.md @@ -13,9 +13,9 @@ pages: 423-448 # Part II ## Chapter 23\ - Bones and Sinew {#Heading1} + Bones and Sinew -### At the Very Heart of Standard PC Graphics {#Heading2} +### At the Very Heart of Standard PC Graphics The VGA is unparalleled in the history of computer graphics, for it is by far the most widely-used graphics standard ever, the closest we may @@ -50,7 +50,7 @@ well underway. We'll start our exploration with a quick overview of the VGA, and then we'll dive right in and get a taste of what the VGA can do. -### The VGA {#Heading3} +### The VGA The VGA is the baseline adapter for modern IBM PC compatibles, present in virtually every PC sold today or in the last several years. (Note @@ -103,7 +103,7 @@ rather to start you down the road to understanding the VGA. Let's begin. -### An Introduction to VGA Programming {#Heading4} +### An Introduction to VGA Programming Most discussions of the VGA start out with a traditional "Here's a block diagram of the VGA" approach, with lists of registers and statistics. @@ -128,7 +128,7 @@ conceptual level, letting the code itself demonstrate the implementation details. We'll return to many of these concepts in more depth later in this book. -### At the Core {#Heading5} +### At the Core A little background is necessary before we're ready to examine Listing 23.1. The VGA is built around four functional blocks, named the CRT @@ -247,7 +247,7 @@ fixed. Moreover, a great deal of graphics software now uses word > course, this method only works if the GC Index register remains > unchanged throughout the loop. -#### Linear Planes and True VGA Modes {#Heading6} +#### Linear Planes and True VGA Modes The VGA's memory is organized as four 64K planes. Each of these planes is a linear bitmap; that is, each byte from a given plane controls eight @@ -867,7 +867,7 @@ cseg ends end start ``` -#### Smooth Panning {#Heading7} +#### Smooth Panning The first thing you'll notice upon running the sample program is the remarkable smoothness with which the display pans from side-to-side and @@ -972,7 +972,7 @@ should be set to 1. > reinventing the wheel and because the BIOS may well mask > incompatibilities between the IBM VGA and VGA clones. -#### Color Plane Manipulation {#Heading8} +#### Color Plane Manipulation The VGA provides a considerable amount of hardware assistance for manipulating the four display memory planes. Two features illustrated by @@ -1050,7 +1050,7 @@ first pass; the VGA is a complicated beast, and learning about it is an iterative process. We'll be going over these features again, in different contexts, over the course of the rest of this book. -#### Page Flipping {#Heading9} +#### Page Flipping When animated graphics are drawn directly on the screen, with no intermediate frame-composition stage, the image typically flickers @@ -1156,7 +1156,7 @@ designing programs for the VGA. To see the program run in 640x200 16-color mode, comment out the `EQU` line for `MEDRES_VIDEO_MODE`. -### The Hazards of VGA Clones {#Heading10} +### The Hazards of VGA Clones Earlier, I said that any VGA that doesn't support the features and functionality covered in this book can't properly be called VGA @@ -1186,7 +1186,7 @@ is that if you're going to use oversized virtual bitmaps and pan around them, you should take great care to test your software on a wide variety of VRAM- and DRAM-based VGAs. -### Just the Beginning {#Heading11} +### Just the Beginning That pretty well covers the important points of the sample VGA program in Listing 23.1. There are many VGA features we didn't even touch on, @@ -1196,7 +1196,7 @@ VGA's resources, and in general to give you an initial sense of what VGA programming is like. Starting with the next chapter, we'll begin to explore the VGA systematically, on a more detailed basis. -### The Macro Assembler {#Heading12} +### The Macro Assembler The code in this book is written in both C and assembly. I think C is a good development environment, but I believe that often the best code diff --git a/chapter-24.md b/chapter-24.md index 576bee9..6f89794 100644 --- a/chapter-24.md +++ b/chapter-24.md @@ -11,9 +11,9 @@ pages: 449-460 --- ## Chapter 24\ - Parallel Processing with the VGA {#Heading1} + Parallel Processing with the VGA -### Taking on Graphics Memory Four Bytes at a Time {#Heading2} +### Taking on Graphics Memory Four Bytes at a Time This heading refers to the ability of the VGA chip to manipulate up to four bytes of display memory at once. In particular, the VGA provides @@ -24,7 +24,7 @@ one part of the surprisingly complex data flow architecture of the VGA, but since they're involved in almost all memory access operations, they're a good place to begin. -### VGA Programming: ALUs and Latches {#Heading3} +### VGA Programming: ALUs and Latches I'm going to begin our detailed tour of the VGA at the heart of the flow of data through the VGA: the four ALUs built into the VGA's Graphics @@ -395,7 +395,7 @@ encountered any particularly valuable applications for AND and OR, but they're the sort of building-block features that could come in handy in just the right context, so keep them in mind. -### Notes on the ALU/Latch Demo Program {#Heading4} +### Notes on the ALU/Latch Demo Program VGA settings such as the logical function select should be restored to their default condition before the BIOS is called to output text or draw diff --git a/chapter-25.md b/chapter-25.md index 27eb760..fe27cc3 100644 --- a/chapter-25.md +++ b/chapter-25.md @@ -11,9 +11,9 @@ pages: 461-479 --- ## Chapter 25\ - VGA Data Machinery {#Heading1} + VGA Data Machinery -### The Barrel Shifter, Bit Mask, and Set/Reset Mechanisms {#Heading2} +### The Barrel Shifter, Bit Mask, and Set/Reset Mechanisms In the last chapter, we examined a simplified model of data flow within the GC portion of the VGA, featuring the latches and ALUs. Now we're @@ -21,7 +21,7 @@ ready to expand that model to include the barrel shifter, bit mask, and the set/reset capabilities, leaving only the write modes to be explored over the next few chapters. -### VGA Data Rotation {#Heading3} +### VGA Data Rotation Figure 25.1 shows an expanded model of GC data flow, featuring the barrel shifter and bit mask circuitry. Let's look at the barrel shifter @@ -60,7 +60,7 @@ I'll demonstrate that application below. In general, though, don't knock yourself out trying to figure out how to work data rotation into your programs—it just isn't all that useful in most cases. -### The Bit Mask {#Heading4} +### The Bit Mask The VGA has bit mask circuitry for each of the four memory planes. The four bit masks operate in parallel and are all driven by the same mask @@ -403,7 +403,7 @@ complex." He's got a point there. -### The VGA's Set/Reset Circuitry {#Heading5} +### The VGA's Set/Reset Circuitry At last we come to the final aspect of data flow through the GC on write mode 0 writes: the set/reset circuitry. Figure 25.3 shows data flow on a @@ -530,7 +530,7 @@ cseg ends end start ``` -#### Setting All Planes to a Single Color {#Heading6} +#### Setting All Planes to a Single Color The set/reset circuitry can be used to force some planes to 0-bits and others to 1-bits during a single write, while letting CPU data go to @@ -680,7 +680,7 @@ cseg ends end start ``` -#### Manipulating Planes Individually {#Heading7} +#### Manipulating Planes Individually Listing 25.4 illustrates the use of set/reset to control only some, rather than all, planes. Here, the set/reset circuitry forces plane 2 to @@ -826,7 +826,7 @@ you force all bits in each plane to either zero or one, or pass CPU data through unchanged, on each write to display memory. As tools go, set/reset is a handy one, and it'll pop up often in this book. -### Notes on Set/Reset {#Heading8} +### Notes on Set/Reset The set/reset circuitry is not active in write modes 1 or 2. The Enable Set/Reset register is inactive in write mode 3, but the Set/Reset @@ -845,7 +845,7 @@ discussed in the next chapter. > selected planes; the set/reset value is then processed in exactly the > same way that CPU data normally is. -### A Brief Note on Word OUTs {#Heading9} +### A Brief Note on Word OUTs In the early days of the EGA and VGA, there was considerable debate about whether it was safe to do word `OUT`s (`OUT DX,AX`) to set diff --git a/chapter-26.md b/chapter-26.md index 4ab5f3b..2d945ef 100644 --- a/chapter-26.md +++ b/chapter-26.md @@ -11,9 +11,9 @@ pages: 481-497 --- ## Chapter 26\ - VGA Write Mode 3 {#Heading1} + VGA Write Mode 3 -### The Write Mode That Grows on You {#Heading2} +### The Write Mode That Grows on You Over the last three chapters, we've covered the VGA's write path from stem to stern—with one exception. Thus far, we've only looked at how @@ -31,7 +31,7 @@ in the next chapter, but right now I want to focus on write mode 3, which can be confusing at first, but turns out to be quite a bit more powerful than one might initially think. -### A Mode Born in Strangeness {#Heading3} +### A Mode Born in Strangeness Write mode 3 is strange indeed, and its use is not immediately obvious. The first time I encountered write mode 3, I understood immediately how @@ -806,7 +806,7 @@ of the X-Sharp library. Nonetheless, the performance benefit of this approach can be a speedup of as much as four times—all thanks to the decidedly quirky but surprisingly powerful and flexible write mode 3. -### A Note on Preserving Register Bits {#Heading4} +### A Note on Preserving Register Bits If you take a quick look, you'll see that the code in Listing 26.1 uses the readable register feature of the VGA to preserve reserved bits and diff --git a/chapter-27.md b/chapter-27.md index b8e363a..46bc392 100644 --- a/chapter-27.md +++ b/chapter-27.md @@ -11,9 +11,9 @@ pages: 499-521 --- ## Chapter 27\ - Yet Another VGA Write Mode {#Heading1} + Yet Another VGA Write Mode -### Write Mode 2, Chunky Bitmaps,and Text-Graphics Coexistence {#Heading2} +### Write Mode 2, Chunky Bitmaps,and Text-Graphics Coexistence In the last chapter, we learned about the markedly peculiar write mode 3 of the VGA, after having spent three chapters learning the ins and outs @@ -31,7 +31,7 @@ ever imagine. Let's start with the easy stuff, write mode 2, and save the read modes for the next chapter. -### Write Mode 2 and Set/Reset {#Heading3} +### Write Mode 2 and Set/Reset Remember how set/reset works? Good, because that's pretty much how write mode 2 works. (You *don't* remember? Well, I'll provide a brief @@ -68,7 +68,7 @@ nonetheless, I suspect that some additional explanation of an admittedly non-obvious mode wouldn't hurt. Let's follow the CPU byte through the VGA in write mode 2, step by step. -#### A Byte's Progress in Write Mode 2 {#Heading4} +#### A Byte's Progress in Write Mode 2 Figure 27.1 shows the write mode 2 data path. The CPU byte comes into the VGA and is split into four separate bits, one for each plane. Bits @@ -130,7 +130,7 @@ on the VGA is to read the Graphics Mode register, mask off bits 1 and 0, OR in 00000010b (02H), and write the result back to the Graphics Mode register, thereby leaving the other bits in the register undisturbed. -#### Copying Chunky Bitmaps to VGA Memory Using Write Mode 2 {#Heading5} +#### Copying Chunky Bitmaps to VGA Memory Using Write Mode 2 Let's take a look at two examples of write mode 2 in action. Listing 27.1 presents a program that uses write mode 2 to copy a graphics image @@ -406,7 +406,7 @@ to illustrate the mechanics of write mode 2. > time, nasty transient color effects can occur as one plane becomes > visibly changed before other planes have been modified. -#### Drawing Color-Patterned Lines Using Write Mode 2 {#Heading6} +#### Drawing Color-Patterned Lines Using Write Mode 2 A more serviceable use of write mode 2 is shown in the program presented in Listing 27.2. The program draws multicolored horizontal, vertical, @@ -764,7 +764,7 @@ Code ends end Start ``` -### When to Use Write Mode 2 and When to Use Set/Reset {#Heading7} +### When to Use Write Mode 2 and When to Use Set/Reset As indicated earlier, write mode 2 and set/reset are functionally interchangeable. Write mode 2 lends itself to more efficient @@ -785,7 +785,7 @@ the value written to some planes to a fixed value while allowing the CPU byte to modify other planes. This is the mode of operation when set/reset is enabled for some but not all planes. -### Mode 13H—320x200 with 256 Colors {#Heading8} +### Mode 13H—320x200 with 256 Colors I'm going to take a minute—and I do mean a minute—to discuss the programming model for mode 13H, the VGA's 320x200 256-color mode. @@ -803,7 +803,7 @@ program, especially given that some of the listings later in this book, such as the antialiasing code in Chapter F on the companion CD-ROM, use mode 13H. -### Flipping Pages from Text to Graphics and Back {#Heading9} +### Flipping Pages from Text to Graphics and Back A while back, I got an interesting letter from Phil Coleman, of La Jolla, who wrote: diff --git a/chapter-28.md b/chapter-28.md index 6f7b36f..06f8dd1 100644 --- a/chapter-28.md +++ b/chapter-28.md @@ -11,9 +11,9 @@ pages: 523-537 --- ## Chapter 28\ - Reading VGA Memory {#Heading1} + Reading VGA Memory -### Read Modes 0 and 1, and the Color Don't Care Register {#Heading2} +### Read Modes 0 and 1, and the Color Don't Care Register Well, it's taken five chapters, but we've finally covered the data write path and all four write modes of the VGA. Now it's time to tackle the @@ -27,7 +27,7 @@ straightforward? Well...no. But then, clearing up the mysteries of VGA programming is what this part of the book is all about, so let's get started. -### Read Mode 0 {#Heading3} +### Read Mode 0 Read mode 0 is actually relatively uncomplicated, given that you understand the four-plane nature of the VGA. (If you don't understand @@ -335,7 +335,7 @@ register affects CPU *writes* to VGA memory in any way. > short, whenever the CPU reads VGA memory in any read mode, all four > planes are read and all four latches are always loaded. -### Read Mode 1 {#Heading4} +### Read Mode 1 Read mode 0 is the workhorse read mode, but it's got an annoying limitation: Whenever you want to determine the color of a given pixel in @@ -537,7 +537,7 @@ code ends end Start ``` -### When all Planes "Don't Care" {#Heading5} +### When all Planes "Don't Care" Still and all, there aren't all that many uses for basic color compare operations. There is, however, a genuinely odd application of read mode diff --git a/chapter-29.md b/chapter-29.md index 787f0fe..97ae45d 100644 --- a/chapter-29.md +++ b/chapter-29.md @@ -11,9 +11,9 @@ pages: 539-559 --- ## Chapter 29\ - Saving Screens and Other VGA Mysteries {#Heading1} + Saving Screens and Other VGA Mysteries -### Useful Nuggets from the VGA Zen File {#Heading2} +### Useful Nuggets from the VGA Zen File There are a number of VGA graphics topics that aren't quite involved enough to warrant their own chapters, yet still cause a fair amount of @@ -25,7 +25,7 @@ writing VGA control registers. That's a lot of ground to cover, so let's get started! -### Saving and Restoring EGA and VGA Screens {#Heading3} +### Saving and Restoring EGA and VGA Screens The memory architectures of EGAs and VGAs are similar enough to treat both together in this regard. The basic principle for saving EGA and VGA @@ -400,7 +400,7 @@ directly. > VGA, of course, you can just read the registers out before you change > them, then put them back the way you found them when you're done. -### 16 Colors out of 64 {#Heading4} +### 16 Colors out of 64 How does one produce the 64 colors from which the 16 colors displayed by the EGA can be chosen? The answer is simple enough: There's a BIOS @@ -770,7 +770,7 @@ Code ends end Start ``` -### Overscan {#Heading5} +### Overscan While we're at it, I'm going to touch on overscan. Overscan is the color of the border of the display, the rectangular area around the edge of @@ -789,7 +789,7 @@ subfunction 1. > border in all modes; all you need do is set the overscan color on any > VGA to see the border. -### A Bonus Blanker {#Heading6} +### A Bonus Blanker An interesting bonus: The Attribute Controller provides a very convenient way to blank the screen, in the form of the aforementioned @@ -898,7 +898,7 @@ capabilities, which are supported by another set of BIOS functions, can be used to produce stunning color effects, as we'll see when we cover them starting in Chapter 33. -### Modifying VGA Registers {#Heading7} +### Modifying VGA Registers EGA registers are not readable. VGA registers are readable. This revelation will not come as news to most of you, but many programmers diff --git a/chapter-30.md b/chapter-30.md index f5baf60..ba6867e 100644 --- a/chapter-30.md +++ b/chapter-30.md @@ -11,9 +11,9 @@ pages: 561-585 --- ## Chapter 30\ - Video Est Omnis Divisa {#Heading1} + Video Est Omnis Divisa -### The Joys and Galling Problems of Using Split Screens on the EGA and VGA {#Heading2} +### The Joys and Galling Problems of Using Split Screens on the EGA and VGA The ability to split the screen into two largely independent portions one—displayed above the other on the screen—is one of the more @@ -33,7 +33,7 @@ we do have some ground to cover. Let's start with the basic operation of the split screen. -### How the Split Screen Works {#Heading3} +### How the Split Screen Works The *operation* of the split screen is simplicity itself. A split screen start scan line value is programmed into two EGA registers or three VGA @@ -97,7 +97,7 @@ line displayed; the safest such approach is to set all bits of the split screen start scan line to 1. (That is, in fact, the split screen start scan line value programmed by the BIOS during a mode set.) -#### The Split Screen in Action {#Heading4} +#### The Split Screen in Action All of these points are illustrated by Listing 30.1. Listing 30.1 fills display memory starting at offset zero (the split screen area of memory) @@ -509,7 +509,7 @@ Code ends end Start ``` -#### VGA and EGA Split-Screen Operation Don't Mix {#Heading5} +#### VGA and EGA Split-Screen Operation Don't Mix You must set the `IS_VGA` equate at the start of Listing 30.1 correctly for the adapter the code will run on in order for the program @@ -537,7 +537,7 @@ highest-resolution mode the VGA and EGA share. That's not the only mode the split screen works in, however. In fact, it works in *all* modes, as we'll see later. -### Setting the Split-Screen-Related Registers {#Heading6} +### Setting the Split-Screen-Related Registers Setting the split-screen-related registers is not as simple a matter as merely outputting the right values to the right registers; timing is @@ -585,7 +585,7 @@ aware that the VGA supports 70 Hz frame rates in all non-480-scan-line modes, while the VGA in 480-scan-line-modes and the EGA in all color modes support 60 Hz frame rates. -### The Problem with the EGA Split Screen {#Heading7} +### The Problem with the EGA Split Screen I mentioned earlier that the EGA's split screen is a little buggy. How? you may well ask, particularly given that Listing 30.1 illustrates that @@ -618,7 +618,7 @@ split screen is on. This isn't a bug—it's just one of the many areas in which the VGA's designers learned from the shortcomings of the EGA and went the EGA one better. -### Split Screen and Panning {#Heading8} +### Split Screen and Panning Back in Chapter 23, I presented a program that performed smooth horizontal panning. Smooth horizontal panning consists of two parts: @@ -661,7 +661,7 @@ can be used to produce an attractive "streaming tape" effect in the normal screen while the split screen is used to display non-moving information. -#### The Split Screen and Horizontal Panning: An Example {#Heading9} +#### The Split Screen and Horizontal Panning: An Example Listing 30.2 illustrates the interaction of horizontal smooth panning with the split screen, as well as the suppression of pel panning in the @@ -1107,7 +1107,7 @@ Codeends endStart ``` -### Notes on Setting and Reading Registers {#Heading10} +### Notes on Setting and Reading Registers There are a few interesting points regarding setting and reading registers to be made about Listing 30.2. First, bit 5 of the AC Index @@ -1190,7 +1190,7 @@ What if you wanted to pan faster? Well, you could of course just move two pixels at a time rather than one; I assure you no one will ever notice when you're panning at a rate of 10 or more times per second. -### Split Screens in Other Modes {#Heading11} +### Split Screens in Other Modes So far we've only discussed the split screen in mode 10H. What about other modes? Generally, the split screen works in any mode; the basic @@ -1226,7 +1226,7 @@ noticeable on-screen effects depends on the text displayed by a particular application; for example, there should be no problem if the split screen has a border of blanks on the left side. -### How Safe? {#Heading12} +### How Safe? So, how safe *is* it to use the split screen? My opinion is that it's perfectly safe, although I'd welcome input from people with extensive diff --git a/chapter-31.md b/chapter-31.md index 4c80bfa..e28d9fc 100644 --- a/chapter-31.md +++ b/chapter-31.md @@ -11,9 +11,9 @@ pages: 587-605 --- ## Chapter 31\ - Higher 256-Color Resolution on the VGA {#Heading1} + Higher 256-Color Resolution on the VGA -### When Is 320x200 Really 320x400? {#Heading2} +### When Is 320x200 Really 320x400? One of the more appealing features of the VGA is its ability to display 256 simultaneous colors. Unfortunately, one of the *less* appealing @@ -43,7 +43,7 @@ games use. So. Let's get started. -### Why 320x200? Only IBM Knows for Sure {#Heading3} +### Why 320x200? Only IBM Knows for Sure The first question, of course, is, "How can it be possible to get higher 256-color resolutions out of the VGA?" After all, there were no unused @@ -86,7 +86,7 @@ designing the chip. Whatever the reason, mode 13H is really a 400-scan-line mode masquerading as a 200-scan-line mode, and we can readily end that masquerade. -### 320x400 256-Color Mode {#Heading4} +### 320x400 256-Color Mode Okay, what's so great about 320x400 256-color mode? Two things: easy, safe mode sets and page flipping. @@ -122,7 +122,7 @@ That's why I like 320x400 256-color mode. The next step is to understand how display memory is organized in 320x400 mode, and that's not so simple. -#### Display Memory Organization in 320x400 Mode {#Heading5} +#### Display Memory Organization in 320x400 Mode First, let's look at why display memory must be organized differently in 320x400 256-color mode than in mode 13H. The designers of the VGA @@ -187,7 +187,7 @@ turned off, and selecting byte mode for video data display. All that's done in the `Set320x400Mode` subroutine in Listing 31.1, which we'll discuss next. -#### Reading and Writing Pixels {#Heading6} +#### Reading and Writing Pixels The basic graphics functions in any mode are functions to read and write single pixels. Any more complex function can be built on these @@ -624,7 +624,7 @@ an adequately fast—and often *very* fast—version for 320x400 mode of whatever graphics function you need. If you're not all that concerned with speed, `WritePixel` and `ReadPixel` should meet your needs. -### Two 256-Color Pages {#Heading7} +### Two 256-Color Pages Listing 31.2 demonstrates the two pages of 320x400 256-color mode by drawing slanting color bars in page 0, then drawing color bars slanting @@ -911,7 +911,7 @@ The displays produced by Listing 31.2 make it clear that 320x400 256-color mode can produce effects that are simply not possible in any 16-color mode. -### Something to Think About {#Heading8} +### Something to Think About You can, if you wish, use the display memory organization of 320x400 mode in 320x200 mode by modifying `Set320x400Mode` to leave the diff --git a/chapter-32.md b/chapter-32.md index 5e78510..8757507 100644 --- a/chapter-32.md +++ b/chapter-32.md @@ -11,9 +11,9 @@ pages: 607-622 --- ## Chapter 32\ - Be It Resolved: 360x480 {#Heading1} + Be It Resolved: 360x480 -### Taking 256-Color Modes About as Far as the Standard VGA Can Take Them {#Heading2} +### Taking 256-Color Modes About as Far as the Standard VGA Can Take Them In the last chapter, we learned how to coax 320x400 256-color resolution out of a standard VGA. At the time, I noted that the VGA was actually @@ -36,7 +36,7 @@ the line-drawing code that we'll develop in Chapter 35. Together, those routines will make a pretty nifty demo of the capabilities of 360x480 256-color mode. -### Extended 256-Color Modes: What's Not to Like? {#Heading3} +### Extended 256-Color Modes: What's Not to Like? When last we left 256-color programming, we had found that the standard 256-color mode, mode 13H, which officially offers 320x200 resolution, @@ -101,7 +101,7 @@ undocumented, offers a better combination of resolution and color; even In other words, 360x480 256-color mode is worth considering—so let's have a look. -### 360x480 256-Color Mode {#Heading4} +### 360x480 256-Color Mode I'm going to start by showing you 360x480 256-color mode in action, after which we'll look at how it works. I suspect that once you see what @@ -585,7 +585,7 @@ significant chunk of the market for your code.) Now that we've seen the wonders of which our new mode is capable, let's take the time to understand how it works. -### How 360x480 256-Color Mode Works {#Heading5} +### How 360x480 256-Color Mode Works In describing 360x480 256-color mode, I'm going to assume that you're familiar with the discussion of 320x400 256-color mode in the last @@ -597,7 +597,7 @@ myself when the goods are just a few page flips (the paper kind) away. stretched in both dimensions. Let's look at the vertical stretching first, since that's the simpler of the two. -#### 480 Scan Lines per Screen: A Little Slower, But No Big Deal {#Heading6} +#### 480 Scan Lines per Screen: A Little Slower, But No Big Deal There's nothing unusual about 480 scan lines; standard modes 11H and 12H support that vertical resolution. The number of scan lines has nothing @@ -620,7 +620,7 @@ isn't *bad*—that's the only refresh rate the EGA ever supported, and the EGA was the industry standard in its time—but it does tend to flicker a little more and so is a little harder on the eyes than 70 Hz. -#### 360 Pixels per Scan Line: No Mean Feat {#Heading7} +#### 360 Pixels per Scan Line: No Mean Feat Converting from 320 to 360 pixels per scan line is more difficult than converting from 400 to 480 scan lines per screen. None of the VGA's @@ -682,7 +682,7 @@ resolution, as well. Once all that's done, the VGA is in 360x480 mode, awaiting our every high-resolution 256-color graphics whim. -#### Accessing Display Memory in 360x480 256-Color Mode {#Heading8} +#### Accessing Display Memory in 360x480 256-Color Mode Setting up for 360x480 256-color mode proved to be quite a task. Is drawing in this mode going to be as difficult? diff --git a/chapter-33.md b/chapter-33.md index 46f3fc4..5282068 100644 --- a/chapter-33.md +++ b/chapter-33.md @@ -11,9 +11,9 @@ pages: 623-636 --- ## Chapter 33\ - Yogi Bear and Eurythmics Confront VGA Colors {#Heading1} + Yogi Bear and Eurythmics Confront VGA Colors -### The Basics of VGA Color Generation {#Heading2} +### The Basics of VGA Color Generation Kevin Mangis wants to know about the VGA's 4-bit to 8-bit to 18-bit color translation. Mansur Loloyan would like to find out how to generate @@ -60,7 +60,7 @@ and the Digital Differential Analyzer," by Tim Paterson, is a good article about fast circle drawing, a topic we'll tackle soon. All in all, the dog days of 1990 were good times for graphics. -### VGA Color Basics {#Heading3} +### VGA Color Basics Briefly put, the VGA color translation circuitry takes in one 4- or 8-bit pixel value at a time and translates it into three 6-bit values, @@ -69,7 +69,7 @@ analog levels and sent to the monitor. Seems simple enough, doesn't it? Unfortunately, nothing is ever that simple on the VGA, and color translation is no exception. -#### The Palette RAM {#Heading4} +#### The Palette RAM The color path in the VGA involves two stages, as shown in Figure 33.1. The first stage fetches a 4-bit pixel from display memory and feeds it @@ -91,7 +91,7 @@ attribute. In 256-color mode, which we'll get to eventually, the palette RAM is not a factor from the programmer's perspective and should be left alone. -#### The DAC {#Heading5} +#### The DAC Once the EGA-compatible palette RAM has fulfilled its karma and performed 4-bit to 6-bit translation on a pixel, the resulting value is @@ -125,7 +125,7 @@ higher the number, the brighter the color, with 0 turning that color off in the pixel and 63 (3FH) making that color maximum brightness. Got all that? -#### Color Paging with the Color Select Register {#Heading6} +#### Color Paging with the Color Select Register "Wait a minute," you say bemusedly. "Aren't you missing some bits between the palette RAM and the DAC?" Indeed I am. The palette RAM puts @@ -173,7 +173,7 @@ out of the circuit and simplifies life something wonderful. The palette RAM exists solely for EGA compatibility, and serves no useful purpose that I know of for VGA-only color programming. -#### 256-Color Mode {#Heading7} +#### 256-Color Mode So far I've spoken only of 16-color modes; what of 256-color modes? @@ -193,7 +193,7 @@ VGA chip to the DAC come from display memory. Therefore, there is no color paging in 256-color mode. Of course, that makes sense given that all 256 DAC registers are simultaneously in use in 256-color mode. -#### Setting the Palette RAM {#Heading8} +#### Setting the Palette RAM The palette RAM can be programmed either directly or through BIOS interrupt 10H, function 10H. I strongly recommend using the BIOS @@ -227,7 +227,7 @@ Having said that, let's leave the palette RAM behind (presumably in a pass-through state) and move on to the DAC, which is the right place to do color translation on the VGA. -#### Setting the DAC {#Heading9} +#### Setting the DAC Like the palette RAM, the DAC registers can be set either directly or through the BIOS. Again, the BIOS should be used whenever possible, but @@ -264,7 +264,7 @@ consists of three bytes; the first byte is a 6-bit red component, the second byte is a 6-bit green component, and the third byte is a 6-bit blue component, as illustrated by Listing 33.1. -### If You Can't Call the BIOS, Who Ya Gonna Call? {#Heading10} +### If You Can't Call the BIOS, Who Ya Gonna Call? Although the palette RAM and DAC registers should be set through the BIOS whenever possible, there are times when the BIOS is not the best @@ -340,7 +340,7 @@ In the meantime, if you can use the BIOS to set the DAC, do so; then you won't have to worry about the real and potential complications of setting the DAC directly. -### An Example of Setting the DAC {#Heading11} +### An Example of Setting the DAC This chapter has gotten about as big as a chapter really ought to be; the VGA color saga will continue in the next few. Quickly, then, Listing diff --git a/chapter-34.md b/chapter-34.md index 6f66592..56a5645 100644 --- a/chapter-34.md +++ b/chapter-34.md @@ -11,9 +11,9 @@ pages: 637-652 --- ## Chapter 34\ - Changing Colors without Writing Pixels {#Heading1} + Changing Colors without Writing Pixels -### Special Effects through Realtime Manipulation of DAC Colors {#Heading2} +### Special Effects through Realtime Manipulation of DAC Colors Sometimes, strange as it may seem, the harder you try, the less you accomplish. Brute force is fine when it suffices, but it does not always @@ -31,7 +31,7 @@ by cleaning up some odds and ends about VGA color. There's a lot to be said about loading the DAC, so let's dive right in and see where the complications lie. -### Color Cycling {#Heading3} +### Color Cycling As we've learned in past chapters, the VGA's DAC contains 256 storage locations, each holding one 18-bit value representing an RGB color @@ -78,7 +78,7 @@ In short, color cycling is really the method of choice for dynamic color effects only in 256-color mode—but, regrettably, color cycling is at its least reliable and capable in that mode, as we'll see next. -### The Heart of the Problem {#Heading4} +### The Heart of the Problem Here's the problem with loading the entire DAC repeatedly: The DAC contains 256 color storage locations, each loaded via either 3 or 4 @@ -122,7 +122,7 @@ let you explore for yourself the extent of the problem on computers in which you're interested. First, though, we must address *another* DAC loading problem: the BIOS. -#### Loading the DAC via the BIOS {#Heading5} +#### Loading the DAC via the BIOS The DAC can be loaded either directly or through subfunctions 10H (for a single DAC register) or 12H (for a block of DAC registers) of the BIOS @@ -194,7 +194,7 @@ cycle by calling the BIOS. Which is not to say that loading the DAC directly is a picnic either, as we'll see next. -#### Loading the DAC Directly {#Heading6} +#### Loading the DAC Directly So we must load the DAC directly in order to perform color cycling. The DAC is loaded directly by sending (with an `OUT` instruction) the @@ -233,7 +233,7 @@ blame me if you get a call from someone who's claims that your program sometimes turns their screen into something resembling month-old yogurt. It's not really your fault, of course—but try explaining that to *them!* -### A Test Program for Color Cycling {#Heading7} +### A Test Program for Color Cycling Anyway, the choice of how to load the DAC is yours. Given that I'm not providing you with any hard-and-fast rules (mainly because there don't @@ -584,7 +584,7 @@ force isn't appropriate to the task of color cycling. That doesn't mean that color cycling can't be used, just that subtler approaches must be employed. Let's look at some of those alternatives. -### Color Cycling Approaches that Work {#Heading8} +### Color Cycling Approaches that Work First of all, I'd like to point out that when color cycling does work, it's a thing of beauty. Assemble Listing 34.1 so that it doesn't use the @@ -680,7 +680,7 @@ That's what *I'd* do. Don't let yourself be held back by my limited imagination, though! Color cycling may be the most complicated of all the color control techniques, but it's also the most powerful. -### Odds and Ends {#Heading9} +### Odds and Ends In my experience, when relying on the autoincrementing feature while loading the DAC, the Write Index register wraps back from 255 to 0, and @@ -694,7 +694,7 @@ understand exactly how your resources behave, and I never know when one of you might come up with a serviceable application for any particular quirk. -#### The DAC Mask {#Heading10} +#### The DAC Mask There's one register in the DAC that I haven't mentioned yet, the DAC Mask register at 03C6H. The operation of this register is simple but @@ -710,7 +710,7 @@ DAC location 0 is looked up for every pixel, and the entire screen displays the color stored in DAC location 0. This makes setting the DAC Mask register to 0 a quick and easy way to blank the screen. -#### Reading the DAC {#Heading11} +#### Reading the DAC The DAC can be read directly, via the DAC Read Index register at 3C7H and the DAC Data register at 3C9H, in much the same way as it can be @@ -742,7 +742,7 @@ conditionally assembling to either guard against interrupts or not and to use `REP INSB` or not. As you can see, reading the DAC settings is very much symmetric with setting the DAC. -#### Cycling Down {#Heading12} +#### Cycling Down And so, at long last, we come to the end of our discussion of color control on the VGA. If it has been more complex than anyone might have diff --git a/chapter-35.md b/chapter-35.md index 54953c1..af5c073 100644 --- a/chapter-35.md +++ b/chapter-35.md @@ -11,9 +11,9 @@ pages: 653-678 --- ## Chapter 35\ - Bresenham Is Fast, and Fast Is Good {#Heading1} + Bresenham Is Fast, and Fast Is Good -### Implementing and Optimizing Bresenham's Line-Drawing Algorithm {#Heading2} +### Implementing and Optimizing Bresenham's Line-Drawing Algorithm For all the complexity of graphics design and programming, surprisingly few primitive functions lie at the heart of most graphics software. @@ -73,7 +73,7 @@ Notwithstanding, the line-drawing implementation in Listing 35.3 is plenty fast enough for most purposes, so let's get the discussion underway. -### The Task at Hand {#Heading3} +### The Task at Hand There are two important characteristics of any line-drawing function. First, it must draw a reasonable approximation of a line. A computer @@ -124,7 +124,7 @@ floating-point operations, no divides, and no multiplies inside the line-drawing loop. Moreover, it can be implemented with surprisingly little code. -### Bresenham's Line-Drawing Algorithm {#Heading4} +### Bresenham's Line-Drawing Algorithm The key to grasping Bresenham's algorithm is to understand that when drawing an approximation of a line on a finite-resolution display, each @@ -243,7 +243,7 @@ under the name *Computer Graphics: Principles and Practice* integer-only, divide-free version of the algorithm, as well as Pascal code for drawing lines in one of the eight possible octants. -#### Strengths and Weaknesses {#Heading5} +#### Strengths and Weaknesses The overwhelming strength of Bresenham's line-drawing algorithm is speed. With no divides, no floating-point operations, and no need for @@ -266,7 +266,7 @@ acceptance the algorithm is certainly good enough. Then, too, users hate waiting for their computer to finish drawing. By any standard of drawing performance, Bresenham's algorithm excels. -### An Implementation in C {#Heading6} +### An Implementation in C It's time to get down and look at some actual working code. Listing 35.1 is a C implementation of Bresenham's line-drawing algorithm for modes @@ -556,7 +556,7 @@ void main() } ``` -#### Looking at EVGALine {#Heading7} +#### Looking at EVGALine The `EVGALine` function itself performs four operations. `EVGALine` first sets up the VGA's hardware so that all pixels drawn will be in the @@ -654,7 +654,7 @@ modularity would improve, speed would suffer markedly. ![**Figure 35.5**  *EVGALine's decision logic.*](images/35-05.jpg) -#### Drawing Each Line {#Heading8} +#### Drawing Each Line The `Octant0` and `Octant1` functions draw lines for which |`DeltaX`| is greater than `DeltaY` and lines for which |`DeltaX`| @@ -685,7 +685,7 @@ coordinate is advanced by either 1 or -1, depending on the value of `XDirection`. (This makes it possible for `Octant1` to draw lines in both octant 1 and octant 2.) -#### Drawing Each Pixel {#Heading9} +#### Drawing Each Pixel At the core of `Octant0` and `Octant1` is a pixel-drawing function, `EVGADot`. `EVGADot` draws a pixel at the specified coordinates in @@ -765,7 +765,7 @@ pixel-drawing function for a different adapter, or a fundamentally different mode such as a 256-color SuperVGA mode, remember to remove the hardware-dependent `outportb` lines in `EVGALine` itself. -### Comments on the C Implementation {#Heading10} +### Comments on the C Implementation `EVGALine` does no error checking whatsoever. My assumption in writing `EVGALine` was that it would be ultimately used as the lowest-level @@ -806,7 +806,7 @@ times the speed? Given which, a high-speed assembly language version of `EVGALine` would seem to be a logical next step. -### Bresenham's Algorithm in Assembly {#Heading11} +### Bresenham's Algorithm in Assembly Listing 35.3 is a high-performance implementation of Bresenham's algorithm, written entirely in assembly language. The code is callable diff --git a/chapter-36.md b/chapter-36.md index 8c68b8a..b52966d 100644 --- a/chapter-36.md +++ b/chapter-36.md @@ -11,9 +11,9 @@ pages: 679-693 --- ## Chapter 36\ - The Good, the Bad, and the Run-Sliced {#Heading1} + The Good, the Bad, and the Run-Sliced -### Faster Bresenham Lines with Run-Length Slice Line Drawing {#Heading2} +### Faster Bresenham Lines with Run-Length Slice Line Drawing Years ago, I worked at a company that asked me to write blazingly fast line-drawing code for an AutoCAD driver. I implemented the basic @@ -107,7 +107,7 @@ algorithm, which just happens to be an excellent example of a minimized state machine. In case you're fuzzy on the good/bad performance thing, that's "good"—as in *fast*. -### Run-Length Slice Fundamentals {#Heading3} +### Run-Length Slice Fundamentals First off, I have a confession to make: I'm not sure that the algorithm I'll discuss is actually, precisely Bresenham's run-length slice @@ -171,7 +171,7 @@ redundant work is preferable. ![**Figure 36.3**  *Runs in a slope 1/3.5 line.*](images/36-03.jpg) -### Run-Length Slice Implementation {#Heading4} +### Run-Length Slice Implementation We know that for any line, a given run will always be one of two possible lengths. How, though, do we know which length to select? @@ -242,7 +242,7 @@ actually write the pixel to display memory. That's good. -### Run-Length Slice Details {#Heading5} +### Run-Length Slice Details A couple of run-length slice implementation details yet remain. First is the matter of how error-term turnover is detected. This is done in much diff --git a/chapter-37.md b/chapter-37.md index 65f9625..a90941e 100644 --- a/chapter-37.md +++ b/chapter-37.md @@ -11,9 +11,9 @@ pages: 695-706 --- ## Chapter 37\ - Dead Cats and Lightning Lines {#Heading1} + Dead Cats and Lightning Lines -### Optimizing Run-Length Slice Line Drawing in a Major Way {#Heading2} +### Optimizing Run-Length Slice Line Drawing in a Major Way As I write this, the wife, the kid, and I are in the throes of yet another lightning-quick transcontinental move, this time to Redmond, @@ -56,7 +56,7 @@ Okay, but what's the point? The point is, if it isn't broken, don't fix it. And if it is broken, maybe that's all right, too. Which brings us, neat as a pin, to the topic of drawing lines in a serious hurry. -### Fast Run-Length Slice Line Drawing {#Heading3} +### Fast Run-Length Slice Line Drawing In the last chapter, we examined the principles of run-length slice line drawing, which draws lines a run at a time rather than a pixel at a @@ -418,7 +418,7 @@ _LineDraw endp end ``` -#### How Fast Is Fast? {#Heading4} +#### How Fast Is Fast? Your first question is likely to be the following: Just how fast is Listing 37.1? Is it optimized to the hilt or just pretty fast? The quick @@ -483,7 +483,7 @@ basically a waste of time. Profile before you optimize. -#### Further Optimizations {#Heading5} +#### Further Optimizations Following is a quick tour of some of the many possible further optimizations to Listing 37.1. diff --git a/chapter-38.md b/chapter-38.md index d0869d4..67073bf 100644 --- a/chapter-38.md +++ b/chapter-38.md @@ -11,9 +11,9 @@ pages: 707-721 --- ## Chapter 38\ - The Polygon Primeval {#Heading1} + The Polygon Primeval -### Drawing Polygons Efficiently and Quickly {#Heading2} +### Drawing Polygons Efficiently and Quickly *"Give me but one firm spot on which to stand, and I will move the Earth."* @@ -44,7 +44,7 @@ boost performance into the realm of the sublime. And slow computer graphics is scarcely worth the bother. -### Filled Polygons {#Heading3} +### Filled Polygons A polygon is simply a shape formed by lines laid end to end to form a continuous, closed path. A polygon is filled by setting all pixels @@ -79,7 +79,7 @@ widely useful and will serve well to introduce some of the subtler complexities of polygon drawing, not the least of which is the slippery concept of "inside." -#### Which Side Is Inside? {#Heading4} +#### Which Side Is Inside? The basic principle of polygon filling is decomposing each polygon into a series of horizontal lines, one for each horizontal row of pixels, or @@ -139,7 +139,7 @@ to the boundary lines just won't do for drawing images composed of fitted-together polygons. And because fitting polygons together is exactly what I have in mind, we need a different approach. -#### How Do You Fit Polygons Together? {#Heading5} +#### How Do You Fit Polygons Together? How, then, do you fit polygons together? *Very* carefully. First, the line-tracing algorithm must be adjusted so that it selects only those @@ -185,7 +185,7 @@ together seamlessly. For our purposes, nonoverlapping polygons are the way to go, so let's have at them. -### Filling Non-Overlapping Convex Polygons {#Heading6} +### Filling Non-Overlapping Convex Polygons Without further ado, Listing 38.1 contains a function, `FillConvexPolygon`, that accepts a list of points that describe a @@ -658,7 +658,7 @@ Once the two edges are scan-converted, the whole line list is passed to Finis. -### Oddball Cases {#Heading7} +### Oddball Cases Listing 38.1 handles zero-length segments (multiple vertices at the same location) by ignoring them, which will be useful down the road because diff --git a/chapter-39.md b/chapter-39.md index 40b1aee..8fec731 100644 --- a/chapter-39.md +++ b/chapter-39.md @@ -11,9 +11,9 @@ pages: 723-738 --- ## Chapter 39\ - Fast Convex Polygons {#Heading1} + Fast Convex Polygons -### Filling Polygons in a Hurry {#Heading2} +### Filling Polygons in a Hurry In the previous chapter, we explored the surprisingly intricate process of filling convex polygons. Now we're going to fill them an order of @@ -92,7 +92,7 @@ to understand how things work, especially when they're very visible parts of the software you develop. That said, let's learn more about filling convex polygons. -### Fast Convex Polygon Filling {#Heading3} +### Fast Convex Polygon Filling In addressing the topic of filling convex polygons in the previous chapter, the implementation we came up with met all of our functional @@ -118,7 +118,7 @@ time was spent drawing and the other half was spent tracing the polygon edges (the time spent in `FillConvexPolygon` was relatively minuscule), so we have our choice of where to begin optimizing. -#### Fast Drawing {#Heading4} +#### Fast Drawing Let's start with drawing, which is easily sped up. The previous chapter's code used a double-nested loop that called a draw-pixel @@ -239,7 +239,7 @@ time. There are cycles yet to be had in the drawing code, but as tracing polygon edges now takes 92 percent of the polygon filling time, it's logical to optimize the tracing code next. -#### Fast Edge Tracing {#Heading5} +#### Fast Edge Tracing There's no secret as to why last chapter's `ScanEdge` was so slow: It used floating point calculations. One secret of fast graphics is using @@ -401,7 +401,7 @@ void ScanEdge(int X1, int Y1, int X2, int Y2, int SetXStart, } ``` -### The Finishing Touch: Assembly Language {#Heading6} +### The Finishing Touch: Assembly Language The C implementation in Listing 39.2 is now nearly 20 times as fast as the original, which is good enough for most purposes. Still, it requires @@ -532,7 +532,7 @@ _DrawHorizontalLineList endp end ``` -#### Maximizing REP STOS {#Heading7} +#### Maximizing REP STOS Listing 39.3 doesn't take the easy way out and use `REP STOSB` to fill each scan line; instead, it uses `REP STOSW` to fill as many pixel @@ -549,7 +549,7 @@ word-at-a-time filling, resulting in extra overhead. For very small or narrow polygons, that overhead might overwhelm the advantage of drawing a word at a time, making plain old `REP STOSB` faster. -### Faster Edge Tracing {#Heading8} +### Faster Edge Tracing Finally, Listing 39.4 is an assembly language version of `ScanEdge`. Listing 39.4 is a relatively straightforward translation from C to diff --git a/chapter-40.md b/chapter-40.md index a751f00..7c3c42e 100644 --- a/chapter-40.md +++ b/chapter-40.md @@ -11,9 +11,9 @@ pages: 739-756 --- ## Chapter 40\ - Of Songs, Taxes, and the Simplicity of Complex Polygons {#Heading1} + Of Songs, Taxes, and the Simplicity of Complex Polygons -### Dealing with Irregular Polygonal Areas {#Heading2} +### Dealing with Irregular Polygonal Areas Every so often, my daughter asks me to sing her to sleep. (If you've ever heard me sing, this may cause you concern about either her hearing @@ -43,7 +43,7 @@ where everything fits together as if preordained. Filling arbitrary polygons is such a case. -### Filling Arbitrary Polygons {#Heading3} +### Filling Arbitrary Polygons In Chapter 38, I described three types of polygons: convex, nonconvex, and complex. *The RenderMan Companion*, a terrific book by Steve Upstill @@ -73,7 +73,7 @@ all polygons; in such a case, the polygon filler will use the slow complex-fill code even if the polygon is, in fact, a convex polygon. In Chapter 41, I'll discuss one way to improve this situation. -#### Active Edges {#Heading4} +#### Active Edges The basic premise of filling a complex polygon is that for a given scan line, we determine all intersections between the polygon's edges and @@ -459,7 +459,7 @@ static void ScanOutAET(int YToScan, int Color) { } ``` -### Complex Polygon Filling: An Implementation {#Heading5} +### Complex Polygon Filling: An Implementation Listing 40.1 just shown presents a function, `FillPolygon()`, that fills polygons of all shapes. If `CONVEX_FILL_LINKED` is defined, @@ -665,7 +665,7 @@ this way elsewhere. The boundary filling approach in Foley and van Dam is similar, but seems to me to not draw all boundary and vertex pixels once and only once. -#### More on Active Edges {#Heading6} +#### More on Active Edges Edges of zero height—horizontal edges and edges defined by two vertices at the same location—never even make it into the GET in Listing 40.1. A @@ -674,7 +674,7 @@ never intersect a scan line; it can only run along the scan line, and the span it runs along is defined not by that edge but by the edges that connect to its endpoints. -#### Performance Considerations {#Heading7} +#### Performance Considerations How fast is Listing 40.1? When drawing triangles on a 20-MHz 386, it's less than one-fifth the speed of the fast convex polygon fill code. @@ -777,7 +777,7 @@ spent sorting the AET. > example of the need to keep an overall perspective when comparing the > theoretical characteristics of various approaches. -### Nonconvex Polygons {#Heading8} +### Nonconvex Polygons Nonconvex polygons can be filled somewhat faster than complex polygons. Because edges never cross or switch positions with other edges once @@ -789,7 +789,7 @@ slopes must be compared to determine which edge is leftmost. This is certainly doable, but because of space limitations and limited performance returns, I haven't implemented this in Listing 40.1. -#### Details, Details {#Heading9} +#### Details, Details Every so often, a programming demon that I'd thought I'd forever laid to rest arises to haunt me once again. A minor example of this—an imp, if diff --git a/chapter-41.md b/chapter-41.md index 2a6b63a..9c0b5b1 100644 --- a/chapter-41.md +++ b/chapter-41.md @@ -11,9 +11,9 @@ pages: 757-771 --- ## Chapter 41\ - Those Way-Down Polygon Nomenclature Blues {#Heading1} + Those Way-Down Polygon Nomenclature Blues -### Names Do Matter when You Conceptualize a Data Structure {#Heading2} +### Names Do Matter when You Conceptualize a Data Structure After I wrote the columns on polygons in *Dr. Dobb's Journal* that became Chapters 38-40, long-time reader Bill Huber wrote to take me to @@ -63,7 +63,7 @@ who could be among your most astute readers—those who already have been trained in the same or a related field." Ditto. Likewise. *D'accord*. And *mea culpa* ; I shall endeavor to watch my language in the future. -### Nomenclature in Action {#Heading3} +### Nomenclature in Action Just to show you how much difference proper description and interchange of ideas can make, consider the case of identifying convex polygons. diff --git a/chapter-42.md b/chapter-42.md index e5cab46..ac1a63a 100644 --- a/chapter-42.md +++ b/chapter-42.md @@ -11,9 +11,9 @@ pages: 773-792 --- ## Chapter 42\ - Wu'ed in Haste; Fried, Stewed at Leisure {#Heading1} + Wu'ed in Haste; Fried, Stewed at Leisure -### Fast Antialiased Lines Using Wu's Algorithm {#Heading2} +### Fast Antialiased Lines Using Wu's Algorithm The thought first popped into my head as I unenthusiastically picked through the salad bar at a local "family" restaurant, trying to decide @@ -74,7 +74,7 @@ be accomplished on inexpensive, mass-market hardware with the proper programming perspective. In short, it's a splendid example of appropriate technology for PCs. -### Wu Antialiasing {#Heading3} +### Wu Antialiasing Antialiasing, as we've been discussing for the past few chapters, is the process of smoothing lines and edges so that they appear less jagged. @@ -128,7 +128,7 @@ possible drawing speed—of an AT-bus VGA. In short, Wu antialiasing is about as fast an antialiased line approach as you could ever hope to find for the VGA. -### Tracing and Intensity in One {#Heading4} +### Tracing and Intensity in One Horizontal, vertical, and diagonal lines do not require Wu antialiasing because they pass through the center of every pixel they meet; such @@ -331,7 +331,7 @@ void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels, } ``` -### Sample Wu Antialiasing {#Heading5} +### Sample Wu Antialiasing The true test of any antialiasing technique is how good it looks, so let's have a look at Wu antialiasing in action. Listing 42.1 is a C @@ -929,7 +929,7 @@ _DrawWuLine endp end ``` -#### Notes on Wu Antialiasing {#Heading6} +#### Notes on Wu Antialiasing Wu antialiasing can be applied to any curve for which it's possible to calculate at each step the positions and intensities of two bracketing diff --git a/chapter-43.md b/chapter-43.md index 79c155c..a9dcf47 100644 --- a/chapter-43.md +++ b/chapter-43.md @@ -11,9 +11,9 @@ pages: 793-815 --- ## Chapter 43\ - Bit-Plane Animation {#Heading1} + Bit-Plane Animation -### A Simple and Extremely Fast Animation Method for Limited Color {#Heading2} +### A Simple and Extremely Fast Animation Method for Limited Color When it comes to computers, my first love is animation. There's nothing quite like the satisfaction of fooling the eye and creating a miniature @@ -87,7 +87,7 @@ resources, and that you can do remarkable things if you understand those resources and come up with creative ways to put them to work at specific tasks. -### Bit-Planes: The Basics {#Heading3} +### Bit-Planes: The Basics The underlying principle of bit-plane animation is extremely simple. The VGA has four separate bit planes in modes 0DH, 0EH, 10H, and 12H. Plane @@ -141,7 +141,7 @@ You bet. ![**Figure 43.3**  *The problem of overlapping colors.*](images/43-03.jpg) -#### Stacking the Palette Registers {#Heading4} +#### Stacking the Palette Registers Suppose that instead of viewing the four bits per pixel coming out of display memory as selecting one of sixteen colors,we view those bits as @@ -217,7 +217,7 @@ chosen to make plane 0 the highest precedence only because it seems simplest to think of plane 0 as appearing in front of plane 1, which is in front of plane 2, which is in front of plane 3. -### Bit-Plane Animation in Action {#Heading5} +### Bit-Plane Animation in Action Without further ado, Listing 43.1 shows bit-plane animation in action. Listing 43.1 animates 13 rather large images (each 32 pixels on a side) @@ -793,7 +793,7 @@ Bit-plane animation with bit-aligned images and internal animation can look truly spectacular. It's a sight worth seeing, particularly for those who doubt the PC's worth when it comes to animation. -### Limitations of Bit-Plane Animation {#Heading6} +### Limitations of Bit-Plane Animation As I've said, bit-plane animation is not perfect. For starters, bit-plane animation can only be used in the VGA's planar modes, modes @@ -877,7 +877,7 @@ the images in a given plane marching along in step in a continuous band. The images could never overlap, so bit-plane animation would produce very high image quality. -### Shearing and Page Flipping {#Heading7} +### Shearing and Page Flipping As Listing 43.1 runs, you may occasionally see an image shear, with the top and bottom parts of the image briefly offset. This is a consequence @@ -942,7 +942,7 @@ conjunction with page flipping, bit-plane animation looks a little better but is slower, and the overall animation scheme is more difficult to implement and perhaps a bit less reliable on some computers. -### Beating the Odds in the Jaw-Dropping Contest {#Heading8} +### Beating the Odds in the Jaw-Dropping Contest Bit-plane animation is neat stuff. Heck, good animation of *any* sort is fun, and the PC is as good a place as any (well, almost any) to make diff --git a/chapter-44.md b/chapter-44.md index bec1ddf..50cc745 100644 --- a/chapter-44.md +++ b/chapter-44.md @@ -11,9 +11,9 @@ pages: 817-837 --- ## Chapter 44\ - Split Screens Save the Page Flipped Day {#Heading1} + Split Screens Save the Page Flipped Day -### 640x480 Page Flipped Animation in 64K...Almost {#Heading2} +### 640x480 Page Flipped Animation in 64K...Almost Almost doesn't count, they say—at least in horseshoes and maybe a few other things. This is especially true in digital circles, where if you @@ -38,7 +38,7 @@ groundwork first. Or maybe a lot of groundwork. No horseshoes here. -#### A Plethora of Challenges {#Heading3} +#### A Plethora of Challenges In its simplest terms, computer animation consists of rapidly redrawing similar images at slightly differing locations, so that the eye @@ -66,7 +66,7 @@ invisible to the user; only the end result should ever be seen. Both of these requirements are met by the program presented in Listings 44.1 and 44.2. -#### A Page Flipping Animation Demonstration {#Heading4} +#### A Page Flipping Animation Demonstration The listings taken together form a sample animation program, in which a single object bounces endlessly off other objects, with instructions and @@ -977,7 +977,7 @@ screens drawn and cross-referencing that to the BIOS timer count periodically, accelerating the overall pace of the animation (moving farther each time and the like) if updates are happening too slowly. -#### Enter the Split Screen {#Heading9} +#### Enter the Split Screen So far, I've discussed page flipping in 640x350 mode. There's a reason for that: 640x350 is the highest-resolution standard mode in which diff --git a/chapter-45.md b/chapter-45.md index af7901f..b6ea0a8 100644 --- a/chapter-45.md +++ b/chapter-45.md @@ -11,9 +11,9 @@ pages: 839-857 --- ## Chapter 45\ - Dog Hair and Dirty Rectangles {#Heading1} + Dog Hair and Dirty Rectangles -### Different Angles on Animation {#Heading2} +### Different Angles on Animation We brought our pets with us when we moved to Seattle. At about the same time, our Golden Retriever, Sam, observed his third birthday. Sam is @@ -55,7 +55,7 @@ joyously knocks down any stranger who makes the mistake of glancing in his direction, and will, quite possibly, be booked any day now on suspicion of homicide by licking. -### Plus ça Change {#Heading3} +### Plus ça Change Okay, you give up. What exactly does this have to do with graphics? I'm glad you asked. The lesson to be learned from Sam, The Dog With A Brain @@ -86,7 +86,7 @@ least one pair of underwear without a single hole in it. Which brings us, deus ex machina and the creek don't rise, to yet another animation method: dirty-rectangle animation. -### VGA Access Times {#Heading4} +### VGA Access Times Actually, before we get to dirty rectangles, I'd like to take you through a quick refresher on VGA memory and I/O access times. I want to @@ -162,7 +162,7 @@ memory if you don't have to. Write each pixel once and only once. It is indeed a strange concept: The key to fast graphics is staying away from the graphics adapter as much as possible. -### Dirty-Rectangle Animation {#Heading5} +### Dirty-Rectangle Animation The relative slowness of VGA hardware is part of the appeal of the technique that I call "dirty-rectangle" animation, in which a complete @@ -192,7 +192,7 @@ solves these problems. ![**Figure 45.2**  *Dirty rectangle animation.*](images/45-02.jpg) -#### So Why Not Use Page Flipping? {#Heading6} +#### So Why Not Use Page Flipping? Well, then, if we want good visual quality, why not use page flipping? For one thing, not all adapters and all modes support page flipping. The @@ -234,7 +234,7 @@ happen, it's still possible to have the images in the various dirty rectangles show up non-simultaneously. In my experience, this latter phenomenon is not a serious problem, but do be aware of it. -### Dirty Rectangles in Action {#Heading7} +### Dirty Rectangles in Action Listing 45.1 demonstrates dirty-rectangle animation. This is a very simple implementation, in several respects. For one thing, it's written @@ -540,7 +540,7 @@ graphics and by items such as scoreboards and status screens, but look closely and see if the animation region in your favorite game isn't smaller than you thought. -### Hi-Res VGA Page Flipping {#Heading8} +### Hi-Res VGA Page Flipping On a standard VGA, hi-res mode is mode 12H, which offers 640x480 resolution with 16 colors. That's a nice mode, with plenty of pixels, @@ -762,7 +762,7 @@ The 640x400 mode I've described here isn't exactly earthshaking, but it can come in handy for page flipping and CGA emulation, and I'm sure that some of you will find it useful at one time or another. -### Another Interesting Twist on Page Flipping {#Heading9} +### Another Interesting Twist on Page Flipping I've spent a fair amount of time exploring various ways to do animation. I thought I had pegged all the possible ways to do animation: diff --git a/chapter-46.md b/chapter-46.md index 9adc882..1668321 100644 --- a/chapter-46.md +++ b/chapter-46.md @@ -11,9 +11,9 @@ pages: 859-874 --- ## Chapter 46\ - Who Was that Masked Image? {#Heading1} + Who Was that Masked Image? -### Optimizing Dirty-Rectangle Animation {#Heading2} +### Optimizing Dirty-Rectangle Animation Programming is, by and large, a linear process. One statement or instruction follows another, in predictable sequences, with tiny @@ -61,7 +61,7 @@ We're strange thinking machines, but we're the best ones yet invented, and it's worth learning how to tap our full potential. And with that, it's back to dirty-rectangle animation. -#### Dirty-Rectangle Animation, Continued {#Heading3} +#### Dirty-Rectangle Animation, Continued In the last chapter, Introduced the idea of dirty-rectangle animation. This technique is an alternative to page flipping that's capable of @@ -618,7 +618,7 @@ RowLoop3: end ``` -#### Masked Images {#Heading4} +#### Masked Images Masked images are rendered by drawing an object's pixels through a mask; pixels are actually drawn only where the mask specifies that drawing is @@ -654,7 +654,7 @@ one color undrawable. Also, with a transparent color, it's not possible to keep the same base image but use different masks, because the mask information is embedded in the image data. -#### Internal Animation {#Heading5} +#### Internal Animation I've added another feature essential to producing convincing animation: *internal animation*, which is the process of changing the appearance of @@ -720,7 +720,7 @@ minimal cost. You might then decide to ignore overlapped drawing between different images, which tends to be both less common and more expensive to identify and handle. -#### Drawing Order and Visual Quality {#Heading7} +#### Drawing Order and Visual Quality A final note on dirty-rectangle animation concerns the quality of the displayed screen image. In the last chapter, we simply stuffed dirty diff --git a/chapter-47.md b/chapter-47.md index bcdcf28..d221aae 100644 --- a/chapter-47.md +++ b/chapter-47.md @@ -11,9 +11,9 @@ pages: 875-893 --- ## Chapter 47\ - Mode X: 256-Color VGA Magic {#Heading1} + Mode X: 256-Color VGA Magic -### Introducing the VGA's Undocumented "Animation-Optimal" Mode {#Heading2} +### Introducing the VGA's Undocumented "Animation-Optimal" Mode At a book signing for my book *Zen of Code Optimization*, an attractive young woman came up to me, holding my book, and said, "You're Michael @@ -60,7 +60,7 @@ two chapters are based on the *DDJ* columns that started it all back in spawned a ton of games, and about which I still regularly get letters and e-mail. Ladies and gentlemen, I give you...Mode X. -### What Makes Mode X Special? {#Heading3} +### What Makes Mode X Special? Consider the strange case of the VGA's 320x256-color mode—Mode X—which is undeniably complex to program and isn't even documented by IBM—but @@ -143,7 +143,7 @@ animation program that shows many of the features of Mode X in action. The mode set code is the logical place to begin. -### Selecting 320x240 256-Color Mode {#Heading4} +### Selecting 320x240 256-Color Mode We could, if we wished, write our own mode set code for Mode X from scratch—but why bother? Instead, we'll let the BIOS do most of the work @@ -440,7 +440,7 @@ _ReadPixelX endp end ``` -### Designing from a Mode X Perspective {#Heading5} +### Designing from a Mode X Perspective Listing 47.4 shows Mode X rectangle fill code. The plane is selected for each pixel in turn, with drawing cycling from plane 0 to plane 3, then @@ -711,7 +711,7 @@ _FillRectangleX endp end ``` -### Hardware Assist from an Unexpected Quarter {#Heading6} +### Hardware Assist from an Unexpected Quarter Listing 47.5 illustrates the benefits of designing code from a Mode X perspective; this is the software aspect of Mode X optimization, which diff --git a/chapter-48.md b/chapter-48.md index dd01efd..565029f 100644 --- a/chapter-48.md +++ b/chapter-48.md @@ -11,9 +11,9 @@ pages: 895-911 --- ## Chapter 48\ - Mode X Marks the Latch {#Heading1} + Mode X Marks the Latch -### The Internals of Animation's Best Video Display Mode {#Heading2} +### The Internals of Animation's Best Video Display Mode In the previous chapter, I introduced you to what I call Mode X, an undocumented 320x240 256-color mode of the VGA. Mode X is distinguished @@ -320,7 +320,7 @@ handle, because the latches are four pixels wide; one possible solution is expanding such patterns via repetition until they are multiple-of-four widths.) -### Allocating Memory in Mode X {#Heading3} +### Allocating Memory in Mode X Listing 48.2 raises some interesting questions about the allocation of display memory in Mode X. In Listing 48.2, whenever a pattern is to be @@ -365,7 +365,7 @@ the screen, with the screen becoming a scrolling window onto that larger bitmap. This technique has been used to good effect in a number of animated games, with and without the use of Mode X. -### Copying Pixel Blocks within Display Memory {#Heading4} +### Copying Pixel Blocks within Display Memory Another fine use for the latches is copying pixels from one place in display memory to another. Whenever both the source and the destination @@ -588,7 +588,7 @@ memory-to-display memory copy routine can do us any good, we must have a way to get pixel patterns from system memory into display memory, so that they can then be copied with the fast copy routine. -#### Copying to Display Memory {#Heading5} +#### Copying to Display Memory The final piece of the puzzle is the system memory to display-memory-copy-routine shown in Listing 48.4. This routine assumes @@ -732,7 +732,7 @@ _CopySystemToScreenX endp end ``` -### Who Was that Masked Image Copier? {#Heading6} +### Who Was that Masked Image Copier? At this point, it's getting to be time for us to take all the Mode X tools we've developed, together with one more tool—masked image diff --git a/chapter-49.md b/chapter-49.md index f602098..9ed650e 100644 --- a/chapter-49.md +++ b/chapter-49.md @@ -11,9 +11,9 @@ pages: 913-930 --- ## Chapter 49\ - Mode X 256-Color Animation {#Heading1} + Mode X 256-Color Animation -### How to Make the VGA Really Get up and Dance {#Heading2} +### How to Make the VGA Really Get up and Dance Okay—no amusing stories or informative anecdotes to kick off this chapter; lotta ground to cover, gotta hurry—you're impatient, I can @@ -33,7 +33,7 @@ computing, as Jeff Duntemann's writings make manifest. No lighthearted fluff for us; we have real work to do, for today we animate with 256 colors in Mode X. -### Masked Copying {#Heading3} +### Masked Copying Over the past two chapters, we've put together most of the tools needed to implement animation in the VGA's undocumented 320x240 256-color Mode @@ -212,7 +212,7 @@ _CopySystemToScreenMaskedX endp end ``` -#### Faster Masked Copying {#Heading4} +#### Faster Masked Copying In the previous chapter we saw how the VGA's latches can be used to copy four pixels at a time from one area of display memory to another in Mode @@ -511,7 +511,7 @@ typedef struct { } MaskedImage; ``` -#### Notes on Masked Copying {#Heading5} +#### Notes on Masked Copying Listings 49.1 and 49.2, like all Mode X code I've presented, perform no clipping, because clipping code would complicate the listings too much. @@ -541,7 +541,7 @@ used separate parameters for simplicity and flexibility. > instruction, and most VGAs respond to `OUT`s much more slowly than to > display memory writes.) -### Animation {#Heading6} +### Animation Gosh. There's just no way I can discuss high-level animation fundamentals in any detail here; I could spend an entire (and entirely @@ -564,7 +564,7 @@ Some of the code in this chapter was adapted for Mode X from the code in Chapter 44—yet another reason to read that chapter before finishing this one. -### Mode X Animation in Action {#Heading7} +### Mode X Animation in Action Listing 49.5 ties together everything I've discussed about Mode X so far in a compact but surprisingly powerful animation package. Listing 49.5 @@ -893,7 +893,7 @@ _ShowPage endp end ``` -### Works Fast, Looks Great {#Heading8} +### Works Fast, Looks Great We now end our exploration of Mode X, although we'll use it again shortly for 3-D animation. Mode X admittedly has its complexities; diff --git a/chapter-50.md b/chapter-50.md index 15bb9ae..f3a3cb2 100644 --- a/chapter-50.md +++ b/chapter-50.md @@ -11,9 +11,9 @@ pages: 931-949 --- ## Chapter 50\ - Adding a Dimension {#Heading1} + Adding a Dimension -### 3-D Animation Using Mode X {#Heading2} +### 3-D Animation Using Mode X When I first started programming micros, more than 11 years ago now, there wasn't much money in it, or visibility, or anything you could call @@ -84,7 +84,7 @@ In a sense, I've saved the best for last, because, to my mind, real-time be done with a computer—and because, with today's hardware, it can in fact be done. Nay, it can be done amazingly well. -### References on 3-D Drawing {#Heading3} +### References on 3-D Drawing There are several good sources for information about 3-D graphics. Foley and van Dam's *Computer Graphics: Principles and Practice* (Second @@ -113,7 +113,7 @@ you're just starting out, you might want to look at one and see if it helps you bridge the gap between the theory and implementation of 3-D graphics. -### The 3-D Drawing Pipeline {#Heading4} +### The 3-D Drawing Pipeline Each 3-D object that we'll handle will be built out of polygons that represent the surface of the object. Figure 50.1 shows the stages a @@ -160,7 +160,7 @@ twice as far away as a coordinate of (0,0,-500). ![**Figure 50.2**  *A right-handed coordinate system.*](images/50-02.jpg) -#### Projection {#Heading5} +#### Projection Working backward from the final image, we want to take the vertices of a polygon, as transformed into view space, and project them to 2-D @@ -182,7 +182,7 @@ projected X and Y coordinates to integers, appropriately clipped and adjusted as necessary to center the origin on the screen or otherwise map the image into a window, if desired. -#### Translation {#Heading6} +#### Translation *Translation* means adding X, Y, and Z offsets to a coordinate to move it linearly through space. Translation is as simple as it seems; it @@ -193,7 +193,7 @@ the object may be located anywhere. ![**Figure 50.3**  *Perspective projection.*](images/50-03.jpg) -#### Rotation {#Heading7} +#### Rotation *Rotation* is the process of circularly moving coordinates around the origin. For our present purposes, it's necessary only to rotate objects @@ -225,7 +225,7 @@ There's much more to be said about transformations and the supporting matrix math, but, in the interests of getting to working code in this chapter, I'll leave that to be discussed as the need arises. -### A Simple 3-D Example {#Heading8} +### A Simple 3-D Example At this point, we know enough to be able to put together a simple working 3-D animation example. The example will do nothing more @@ -755,7 +755,7 @@ void main() { } ``` -#### Notes on the 3-D Animation Example {#Heading9} +#### Notes on the 3-D Animation Example The sample program transforms the polygon's vertices from object space to world space to view space to the screen, as described earlier. In @@ -812,7 +812,7 @@ it rotates. This is temporal aliasing at its finest! We won't address antialiasing further, realtime antialiasing being decidedly nontrivial, but this should give you an idea of why antialiasing is so desirable. -### An Ongoing Journey {#Heading10} +### An Ongoing Journey In the next chapter, we'll assign fronts and backs to polygons, and start drawing only those that are facing the viewer. That will enable us diff --git a/chapter-51.md b/chapter-51.md index bc16172..f68c860 100644 --- a/chapter-51.md +++ b/chapter-51.md @@ -11,9 +11,9 @@ pages: 951-967 --- ## Chapter 51\ - Sneakers in Space {#Heading1} + Sneakers in Space -### Using Backface Removal to Eliminate Hidden Surfaces {#Heading2} +### Using Backface Removal to Eliminate Hidden Surfaces As I'm fond of pointing out, computer animation isn't a matter of mathematically exact modeling or raw technical prowess, but rather of @@ -68,7 +68,7 @@ being tipped off—which is, of course, the whole point.) If it's good enough for George Lucas, it's good enough for us. And with that, let's resume our quest for realtime 3-D animation on the PC. -### One-sided Polygons: Backface Removal {#Heading3} +### One-sided Polygons: Backface Removal In the previous chapter, we implemented the basic polygon drawing pipeline, transforming a polygon all the way from its basic definition @@ -181,7 +181,7 @@ the reason it's preferable to work in screen space rather than screen coordinates (which suffer from rounding problems), speed considerations aside. -#### Backface Removal in Action {#Heading4} +#### Backface Removal in Action Listings 51.1 through 51.5 together form a program that rotates a solid cube in real-time under user control. Listing 51.1 is the main program; @@ -514,7 +514,7 @@ approach; over two-thirds of the overall time is spent in floating-point calculations, and it's there that we'll begin to attack the performance bottleneck we find ourselves up against. -### Incremental Transformation {#Heading5} +### Incremental Transformation Listing 51.4 contains three functions; each concatenates an additional rotation around one of the three axes to an existing rotation. To @@ -690,7 +690,7 @@ extern int DisplayedPage, NonDisplayedPage; extern struct Rect EraseRect[]; ``` -### A Note on Rounding Negative Numbers {#Heading6} +### A Note on Rounding Negative Numbers In the previous chapter, I added 0.5 and truncated in order to round values from floating-point to integer format. Here, in Listing 51.2, @@ -698,7 +698,7 @@ I've switched to adding 0.5 and using the `floor()` function. For positive values, the two approaches are equivalent; for negative values, only the `floor()` approach works properly. -### Object Representation {#Heading7} +### Object Representation Each object consists of a list of vertices and a list of faces, with the vertices of each face defined by pointers into the vertex list; this diff --git a/chapter-52.md b/chapter-52.md index aebef15..d49f27e 100644 --- a/chapter-52.md +++ b/chapter-52.md @@ -11,9 +11,9 @@ pages: 969-987 --- ## Chapter 52\ - Fast 3-D Animation: Meet X-Sharp {#Heading1} + Fast 3-D Animation: Meet X-Sharp -### The First Iteration of a Generalized 3-D Animation Package {#Heading2} +### The First Iteration of a Generalized 3-D Animation Package Across the lake from Vermont, a few miles into upstate New York, the Ausable River has carved out a fairly impressive gorge known as "Ausable @@ -68,7 +68,7 @@ easy to add new and different sorts of objects. Taken together, these alterations will let us start to do some really interesting real-time animation. -### This Chapter's Demo Program {#Heading3} +### This Chapter's Demo Program Three-dimensional animation is a complicated business, and it takes an astonishing amount of functionality just to get off the launching pad: @@ -795,7 +795,7 @@ extern Object *ObjectList[]; extern Point3 CubeVerts[]; ``` -### A New Animation Framework: X-Sharp {#Heading4} +### A New Animation Framework: X-Sharp Listings 52.1 through 52.10 shown earlier represent not merely faster animation in library form, but also a nearly complete, extensible, @@ -849,7 +849,7 @@ If you're intending to use X-Sharp in a real project, use the most recent version to be sure that you avail yourself of all new features and bug fixes. -### Three Keys to Realtime Animation Performance {#Heading5} +### Three Keys to Realtime Animation Performance As of the previous chapter, we were at the point where we could rotate, move, and draw a solid cube in real time. Not too shabby...but the code @@ -912,7 +912,7 @@ Borland C++, and we're *still* doing sine and cosine via the floating-point emulator. Happily, we're still nowhere near the upper limit on the animation potential of the PC. -#### Drawbacks {#Heading6} +#### Drawbacks The techniques we've used to turbocharge 3-D animation are very powerful, but there's a dark side to them as well. Obviously, native 386 @@ -939,7 +939,7 @@ the viewer moves, so there's no chance for cumulative error. 3-D clipping with a front clip plane of -1 or less can prevent divide overflow. -#### Where the Time Goes {#Heading7} +#### Where the Time Goes The distribution of execution time in the animation code is no longer wildly biased toward transformation, but sine and cosine are certainly diff --git a/chapter-53.md b/chapter-53.md index c0578ff..b0c24a9 100644 --- a/chapter-53.md +++ b/chapter-53.md @@ -11,9 +11,9 @@ pages: 989-1003 --- ## Chapter 53\ - Raw Speed and More {#Heading1} + Raw Speed and More -### The Naked Truth About Speed in 3-D Animation {#Heading2} +### The Naked Truth About Speed in 3-D Animation Years ago, this friend of mine—let's call him Bert—went to Hawaii with three other fellows to celebrate their graduation from high school. This @@ -52,7 +52,7 @@ elevator doors opened again. On the lobby. And with that, we come to this chapter's topics: raw speed and hidden surfaces. -### Raw Speed, Part 1: Assembly Language {#Heading3} +### Raw Speed, Part 1: Assembly Language I would like to state, here and for the record, that I am not an assembly language fanatic. Frankly, I prefer programming in C; assembly @@ -478,7 +478,7 @@ ret end ``` -### Raw Speed, Part II: Look it Up {#Heading4} +### Raw Speed, Part II: Look it Up It's a funny thing about Turbo Profiler: Time spent in the Borland C++ 80x87 emulator doesn't show up directly anywhere that I can see in the @@ -523,7 +523,7 @@ polygon filling soon, but for the moment, we have more than enough horsepower to have some fun with. First, though, we need one more feature: hidden surfaces. -#### Hidden Surfaces {#Heading5} +#### Hidden Surfaces So far, we've made a number of simplifying assumptions in order to get the animation to look good; for example, all objects must currently be @@ -662,7 +662,7 @@ void SortObjects() } ``` -#### Rounding {#Heading6} +#### Rounding FIXED.ASM contains the equate **ROUNDING-ON**. When this equate is 1, the results of multiplications and divisions are rounded to the nearest @@ -689,7 +689,7 @@ division is performed only in the course of projection, and the results do not accumulate over time, so it would be reasonable to disable rounding for division. -#### Having a Ball {#Heading7} +#### Having a Ball So far in our exploration of 3-D animation, we've had nothing to look at but triangles and cubes. It's time for something a little more visually diff --git a/chapter-54.md b/chapter-54.md index cbf7aaa..05050d8 100644 --- a/chapter-54.md +++ b/chapter-54.md @@ -11,9 +11,9 @@ pages: 1005-1029 --- ## Chapter 54\ - 3-D Shading {#Heading1} + 3-D Shading -### Putting Realistic Surfaces on Animated 3-D Objects {#Heading2} +### Putting Realistic Surfaces on Animated 3-D Objects At the end of the previous chapter, X-Sharp had just acquired basic hidden-surface capability, and performance had been vastly improved @@ -22,7 +22,7 @@ to add quite a bit more: support for 8088 and 80286 PCs, a general color model, and shading. That's an awful lot to cover in one chapter (actually, it'll spill over into the next chapter), so let's get to it! -### Support for Older Processors {#Heading3} +### Support for Older Processors To date, X-Sharp has run on only the 386 and 486, because it uses 32-bit multiply and divide instructions that sub-386 processors don't support. @@ -940,7 +940,7 @@ _ConcatXforms endp end ``` -#### Shading {#Heading4} +#### Shading So far, the polygons out of which our animated objects have been built have had colors of fixed intensities. For example, a face of a cube @@ -1168,7 +1168,7 @@ void DrawPObject(PObject * ObjectToXform) } ``` -#### Shading: Implementation Details {#Heading7} +#### Shading: Implementation Details In order to calculate the cosine of the angle between an incoming light source and a polygon's unit normal, we must first have the polygon's diff --git a/chapter-55.md b/chapter-55.md index e050544..ccf289d 100644 --- a/chapter-55.md +++ b/chapter-55.md @@ -11,9 +11,9 @@ pages: 1031-1044 --- ## Chapter 55\ - Color Modeling in 256-Color Mode {#Heading1} + Color Modeling in 256-Color Mode -### Pondering X-Sharp's Color Model in an RGB State of Mind {#Heading2} +### Pondering X-Sharp's Color Model in an RGB State of Mind Once she turned six, my daughter wanted some fairly sophisticated books read to her*. Wind in the Willows.* *Little House on the Prairie.* @@ -44,7 +44,7 @@ color on a 256-color display adapter such as the VGA. Coincidentally, VGA color modeling just happens to be this chapter's topic, and the place to start is with color modeling in general. -#### A Color Model {#Heading3} +#### A Color Model We've been developing X-Sharp for several chapters now. In the previous chapter, we added illumination sources and shading; that addition makes @@ -340,7 +340,7 @@ To experiment with a different 256-color model in X-Sharp, just change `ModelColorToColorIndex()` to map 24-bit RGB triplets into the palette you've set up. It's that simple, and the results can be striking indeed. -#### A Bonus from the BitMan {#Heading4} +#### A Bonus from the BitMan Finally, a note on fast VGA text, which came in from a correspondent who asked to be referred to simply as the BitMan. The BitMan passed along a diff --git a/chapter-56.md b/chapter-56.md index c3a41ff..d93fcb0 100644 --- a/chapter-56.md +++ b/chapter-56.md @@ -11,9 +11,9 @@ pages: 1046-1059 --- ## Chapter 56\ - Pooh and the Space Station {#Heading1} + Pooh and the Space Station -### Using Fast Texture Mapping to Place Pooh on a Polygon {#Heading2} +### Using Fast Texture Mapping to Place Pooh on a Polygon So, here's where Winnie the Pooh lives: in a space station orbiting Saturn. No, really; I have it straight from my daughter, and an @@ -71,7 +71,7 @@ someone were standing in the window.... The rest is history. -### Principles of Quick-and-Dirty Texture Mapping {#Heading3} +### Principles of Quick-and-Dirty Texture Mapping The key to our texture-mapping approach will be to quickly determine what pixel value to draw for each pixel in the transformed destination @@ -96,7 +96,7 @@ slower. ![**Figure 56.1**  *Using reverse transformation to find the source pixel color.*](images/56-01.jpg) -#### Mapping Textures Made Easy {#Heading4} +#### Mapping Textures Made Easy To understand how we're going to map textures, consider Figure 56.2, which maps a bitmapped image directly onto an untransformed polygon. @@ -190,7 +190,7 @@ the source image.*](images/56-04.jpg) ![**Figure 56.5**  *Mapping a texture onto a narrower polygon.*](images/56-05.jpg) -#### Notes on DDA Texture Mapping {#Heading5} +#### Notes on DDA Texture Mapping That's all there is to quick-and-dirty texture mapping. This technique basically uses a two-stage digital differential analyzer (DDA) approach @@ -247,7 +247,7 @@ performs DDA texture mapping. First, though, I'd like to take a moment to thank Jim Kent, author of Autodesk Animator and a frequent correspondent, for getting me started with the DDA approach. -### Fast Texture Mapping: An Implementation {#Heading6} +### Fast Texture Mapping: An Implementation As you might expect, I've implemented DDA texture mapping in X-Sharp, and the changes are reflected in the X-Sharp archive in this chapter's diff --git a/chapter-57.md b/chapter-57.md index 4ae2415..c67b558 100644 --- a/chapter-57.md +++ b/chapter-57.md @@ -11,9 +11,9 @@ pages: 1061-1075 --- ## Chapter 57\ - 10,000 Freshly Sheared Sheep on the Screen {#Heading1} + 10,000 Freshly Sheared Sheep on the Screen -### The Critical Role of Experience in Implementing Fast, Smooth Texture Mapping {#Heading2} +### The Critical Role of Experience in Implementing Fast, Smooth Texture Mapping I recently spent an hour or so learning how to shear a sheep. Among other things, I learned—in great detail—about the importance of @@ -42,7 +42,7 @@ graphics-programming approaches and algorithms. Second, computer graphics is a matter of illusion, of convincing the eye to see what you want it to see, and that's very much a black art based on experience. -#### Visual Quality: A Black Hole ... Er, Art {#Heading3} +#### Visual Quality: A Black Hole ... Er, Art Pleasing the eye with realtime computer animation is something less than a science, at least at the PC level, where there's a limited color @@ -76,7 +76,7 @@ continuous motion, much like watching a badly flickering movie. > and blend the images together into continuous motion. Only experience > can give you a feel for that sweet spot. -#### Fixed-Point Arithmetic, Redux {#Heading4} +#### Fixed-Point Arithmetic, Redux In the previous chapter I added texture mapping to X-Sharp, but lacked space to explain some of its finer points. I'll pick up the thread now @@ -135,7 +135,7 @@ Experience again: It's the difference between knowing which flaws (like small texture shifts) can reasonably be ignored, and which (like those that produce gaps between polygons) must be avoided at all costs. -#### Texture Mapping: Orientation Independence {#Heading5} +#### Texture Mapping: Orientation Independence The double-DDA texture-mapping code presented in the previous chapter worked adequately, but there were two things about it that left me less @@ -249,7 +249,7 @@ void ScanOutLine(EdgeScan * LeftEdge, EdgeScan * RightEdge) } ``` -#### Mapping Textures across Multiple Polygons {#Heading6} +#### Mapping Textures across Multiple Polygons One of the truly nifty things about double-DDA texture mapping is that it is not limited to mapping a texture onto a single polygon. A single diff --git a/chapter-58.md b/chapter-58.md index a61b4ae..6db4936 100644 --- a/chapter-58.md +++ b/chapter-58.md @@ -11,9 +11,9 @@ pages: 1077-1093 --- ## Chapter 58\ - Heinlein's Crystal Ball, Spock's Brain, and the 9-Cycle Dare {#Heading1} + Heinlein's Crystal Ball, Spock's Brain, and the 9-Cycle Dare -### Using the Whole-Brain Approach to Accelerate Texture Mapping {#Heading2} +### Using the Whole-Brain Approach to Accelerate Texture Mapping I've had the pleasure recently of rereading several of the works of Robert A. Heinlein, and I'm as impressed as I was as a teenager—but in a @@ -81,7 +81,7 @@ it. As Exhibit \#1, I present my experience with speeding up the texture mapper in X-Sharp. -### Texture Mapping Redux {#Heading3} +### Texture Mapping Redux We've spent the previous several chapters exploring the X Sharp graphics library, something I built over time as a serious exercise in 3-D @@ -105,7 +105,7 @@ But 3 jumps *per pixel?* Hmph!" It was the "Hmph" that really got to me. -#### Left-Brain Optimization {#Heading4} +#### Left-Brain Optimization That was the first shot of juice for my optimizer (or at least blow to my ego, which can be just as productive). John went on to say he had @@ -223,7 +223,7 @@ don't you just draw vertical rather than horizontal scanlines?" Why indeed? -#### A 90-Degree Shift in Perspective {#Heading5} +#### A 90-Degree Shift in Perspective As I said earlier, how you look at an optimization problem defines how you'll be able to solve it. In order to boost performance, sometimes @@ -326,7 +326,7 @@ and get caught up in particular implementations; if you bounce your ideas off someone, you may well find them coming back with an unexpected—and welcome—spin. -### That's Nice—But it Sure as Heck Ain't 9 Cycles {#Heading6} +### That's Nice—But it Sure as Heck Ain't 9 Cycles Excellent as Chris's suggestion was, I still had work to do: Listing 58.2 is still more than twice as slow as John Miles's code. @@ -492,7 +492,7 @@ SCANOFFSET = SCANOFFSET + SCANWIDTH ENDM ``` -#### Don't Stop Thinking about Those Cycles {#Heading7} +#### Don't Stop Thinking about Those Cycles Remember what I said at the outset, that knowing something has been done makes it much easier to do? A corollary is that pushing past that point, @@ -580,7 +580,7 @@ every optimization task as if John Miles has just written to inform you that he's made it faster than your wildest dreams, and you'll be amazed at what you can do! -### Texture Mapping Notes {#Heading8} +### Texture Mapping Notes Listing 58.3 contains no 486 pipeline stalls; it has Pentium stalls, but not much can be done for them because of the size prefix on `ADD diff --git a/chapter-59.md b/chapter-59.md index aeffdc2..8da97ac 100644 --- a/chapter-59.md +++ b/chapter-59.md @@ -11,9 +11,9 @@ pages: 1095-1114 --- ## Chapter 59\ - The Idea of BSP Trees {#Heading1} + The Idea of BSP Trees -### What BSP Trees Are and How to Walk Them {#Heading2} +### What BSP Trees Are and How to Walk Them The answer is: Wendy Tucker. @@ -94,7 +94,7 @@ the most from them. Before we begin, I'd like to thank John Carmack, the technical wizard behind DOOM, for generously sharing his knowledge of BSP trees with me. -### BSP Trees {#Heading3} +### BSP Trees A BSP tree is, at heart, nothing more than a tree that subdivides space in order to isolate features of interest. Each node of a BSP tree splits @@ -117,7 +117,7 @@ powerful way to implement Constructive Solid Geometry (CSG). BSP trees can also be used for hit testing, line-of-sight determination, and collision detection. -#### Visibility Determination {#Heading4} +#### Visibility Determination For the time being, I'm going to discuss only one of the many uses of BSP trees: The ability of a BSP tree to allow you to traverse a set of @@ -158,7 +158,7 @@ trees. ![**Figure 59.1**  *The painter's algorithm.*](images/59-01.jpg) -#### Limitations of BSP Trees {#Heading5} +#### Limitations of BSP Trees Powerful as they are, BSP trees aren't perfect. By far the greatest limitation of BSP trees is that they're time-consuming to build, enough @@ -207,7 +207,7 @@ I'll present in the next chapter, which visually depicts the process of spatial subdivision as a BSP tree is constructed, help a great deal with BSP debugging. -### Building a BSP Tree {#Heading6} +### Building a BSP Tree Now that we know a good bit about what a BSP tree is, how it helps in visible surface determination, and what its strengths and weaknesses @@ -280,7 +280,7 @@ treated as a separate wall. As shown in Figure 59.6, each of the split pieces then has a subspace to itself, and each becomes a leaf of the tree. The BSP tree is now complete. -#### Visibility Ordering {#Heading7} +#### Visibility Ordering Now that we've successfully built a BSP tree, you might justifiably be a little puzzled as to how any of this helps with visibility ordering. The @@ -379,7 +379,7 @@ void WalkBSPTree(NODE *pNode) > partition space identically and can't occlude one another, so it > suffices to generate one splitting node for each collinear set. -### Inorder Walks of BSP Trees {#Heading8} +### Inorder Walks of BSP Trees It was implementing BSP trees that got me to thinking about inorder tree traversal. In inorder traversal, the left subtree of each node gets @@ -472,7 +472,7 @@ fully functional model to follow, with all the problems solved, but they can't make the connection between that model and the code they're trying to implement. Why is this? -#### Know It *Cold* {#Heading9} +#### Know It *Cold* The problem is that these people don't understand inorder walking through and through. They understand the concepts of visiting left and @@ -604,7 +604,7 @@ pants. > the model down cold, you can always tell if the implementation is > correct by comparing it with the model. -#### Measure and Learn {#Heading10} +#### Measure and Learn How much difference does all this fuss make, anyway? Listing 59.5 is a sample program that builds a tree, then calls `WalkTree` () to walk it @@ -749,7 +749,7 @@ run fast enough to keep up if you just keep at it. Depths within depths indeed! -### Surfing Amidst the Trees {#Heading11} +### Surfing Amidst the Trees In the next chapter, we'll build a BSP-tree compiler, and after that, we'll put together a rendering system built around the BSP trees the @@ -761,7 +761,7 @@ must investigate at up in the familiar Internet Frequently Asked Questions (FAQ) style, and is very good stuff. -#### Related Reading {#Heading12} +#### Related Reading Foley, J., A. van Dam, S. Feiner, and J. Hughes, *Computer Graphics: Principles and Practice (Second Edition)*, Addison Wesley, 1990, pp. diff --git a/chapter-60.md b/chapter-60.md index 9fae606..7cf1554 100644 --- a/chapter-60.md +++ b/chapter-60.md @@ -11,9 +11,9 @@ pages: 1115-1129 --- ## Chapter 60\ - Compiling BSP Trees {#Heading1} + Compiling BSP Trees -### Taking BSP Trees from Concept to Reality {#Heading2} +### Taking BSP Trees from Concept to Reality As long-time readers of my columns know, I tend to move my family around the country quite a bit. Change doesn't come out of the blue, so there's @@ -103,7 +103,7 @@ optimization. Onward to compiling BSP trees. -### Compiling BSP Trees {#Heading3} +### Compiling BSP Trees As you'll recall from the previous chapter, a BSP tree is nothing more than a series of binary subdivisions that partion space into @@ -139,7 +139,7 @@ choosing splitters shortly, but first let's look at the process of splitting and assigning. To do that, we need to understand parametric lines. -#### Parametric Lines {#Heading4} +#### Parametric Lines We're all familiar with lines described in slope-intercept form, with y as a function of x @@ -190,7 +190,7 @@ indeed. ![**Figure 60.2**  *Line segment storage in the BSP compiler.*](images/60-02.jpg) -#### Parametric Line Clipping {#Heading5} +#### Parametric Line Clipping In order to assign a line segment to one subspace or the other of a splitter, we must somehow figure out whether the line segment straddles @@ -236,7 +236,7 @@ normal; you could calculate the normal as the cross-product of two of the polygon's edges, or precalculate it when you build the world database. -#### The BSP Compiler {#Heading6} +#### The BSP Compiler Listing 60.1 shows the core of a BSP compiler—the code that actually builds the BSP tree. (Note that Listing 60.1 is excerpted from a C++ @@ -526,7 +526,7 @@ copying some global variables that it uses.) The complete code is too large to print here in its entirety, but it's on the CD-ROM in file DDJBSP.ZIP. -### Optimizing the BSP Tree {#Heading7} +### Optimizing the BSP Tree In the previous chapter, I promised that I'd discuss how to go about deciding which wall to use as the splitter at each node in constructing @@ -583,7 +583,7 @@ surfaces that are being considered for that node. In other words, I choose the wall that splits the fewest of the walls in the subspace it's subdividing. -### BSP Optimization: an Undiscovered Country {#Heading8} +### BSP Optimization: an Undiscovered Country Although BSP trees have been around for at least 15 years now, they're still only partially understood and are a ripe area for applied research diff --git a/chapter-61.md b/chapter-61.md index d76bbf3..75779b4 100644 --- a/chapter-61.md +++ b/chapter-61.md @@ -11,9 +11,9 @@ pages: 1131-1144 --- ## Chapter 61\ - Frames of Reference {#Heading1} + Frames of Reference -### The Fundamentals of the Math behind 3-D Graphics {#Heading2} +### The Fundamentals of the Math behind 3-D Graphics Several years ago, I opened a column in *Dr. Dobb's Journal* with a story about singing my daughter to sleep with Beatles' songs. Beatles' @@ -55,7 +55,7 @@ important sort of difference. Before we can talk about transforming between coordinate spaces, however, we need two building blocks: dot products and cross products. -#### 3-D Math {#Heading3} +#### 3-D Math At this point in the book, I was originally going to present a BSP-based renderer, to complement the BSP compiler I presented in the previous @@ -89,7 +89,7 @@ chapter examining these basic tools and some of their 3-D applications. If this is old hat to you, my apologies, and I'll return to BSP-based rendering in the next chapter. -#### Foundation Definitions {#Heading4} +#### Foundation Definitions The dot and cross products themselves are straightforward and require almost no context to understand, but I need to define some terms I'll @@ -142,7 +142,7 @@ For additional information, you might want to check out Foley & van Dam's *Computer Graphics* (ISBN 0-201-12110-7), or the chapters in this book dealing with my X-Sharp 3-D graphics library. -### The Dot Product {#Heading5} +### The Dot Product Now we're ready to move on to the dot product. Given two vectors `U` = [u~1~ u~2~ u~3~] and `V` = [v~1~ v~2~ v~3~], their dot product, @@ -168,7 +168,7 @@ are the lengths of the vectors, as shown in Figure 61.1. Although it's not immediately obvious, equation 3 has a wide variety of applications in 3-D graphics. -#### Dot Products of Unit Vectors {#Heading6} +#### Dot Products of Unit Vectors The simplest case of the dot product is when both vectors are *unit vectors*; that is, when their lengths are both one, as calculated as in @@ -208,7 +208,7 @@ three additions—and no explicit cosine calculations—as where `N`~s~ is the surface unit normal and `D`~l~ is the light unit direction vector, as shown in Figure 61.2. -### Cross Products and the Generation of Polygon Normals {#Heading7} +### Cross Products and the Generation of Polygon Normals One question equation 6 begs is where the surface unit normal comes from. One approach is to store the end of a surface normal as an extra @@ -287,7 +287,7 @@ orthogonal unit vectors, you'll have to normalize the resulting vector; that is, divide each of the vector's components by the length of the vector, to make it a unit long. -### Using the Sign of the Dot Product {#Heading8} +### Using the Sign of the Dot Product The dot product is the cosine of the angle between two vectors, scaled by the magnitudes of the vectors. Magnitudes are always positive, so the @@ -335,7 +335,7 @@ understand the use of the dot product for projection. ![**Figure 61.5**  *Backface culling with the dot product.*](images/61-05.jpg) -### Using the Dot Product for Projection {#Heading9} +### Using the Dot Product for Projection Consider Equation 3 again, but this time make one of the vectors, say `V`, a unit vector. Now the equation reduces to: @@ -407,7 +407,7 @@ void LineIntersectPlane (float *linestart, float *lineend, } ``` -### Rotation by Projection {#Heading10} +### Rotation by Projection We can use the dot product's projection capability to look at rotation in an interesting way. Typically, rotations are represented by matrices. diff --git a/chapter-62.md b/chapter-62.md index 54bce44..895d4e4 100644 --- a/chapter-62.md +++ b/chapter-62.md @@ -11,9 +11,9 @@ pages: 1145-1162 --- ## Chapter 62\ - One Story, Two Rules, and a BSP Renderer {#Heading1} + One Story, Two Rules, and a BSP Renderer -### Taking a Compiled BSP Tree from Logical to Visual Reality {#Heading2} +### Taking a Compiled BSP Tree from Logical to Visual Reality As I've noted before, I'm working on Quake, id Software's follow-up to DOOM. A month or so back, we added page flipping to Quake, and made the @@ -78,7 +78,7 @@ Tools*, AP Professional, ISBN 0-12-627230-1. Onward to rendering from a BSP tree. -#### BSP-based Rendering {#Heading3} +#### BSP-based Rendering For the last several chapters I've been discussing the nature of BSP (Binary Space Partitioning) trees, and in Chapter 60 I presented a @@ -561,7 +561,7 @@ void UpdateWorld() } ``` -#### The Rendering Pipeline {#Heading4} +#### The Rendering Pipeline Conceptually rendering from a BSP tree really is that simple, but the implementation is a bit more complicated. The full rendering pipeline, @@ -582,7 +582,7 @@ detail. Some sources for further reading are *Computer Graphics*, by Foley and van Dam (ISBN 0-201-12110-7), and the *DDJ Essential Books on Graphics Programming* CD. -### Moving the Viewer {#Heading5} +### Moving the Viewer The sample BSP program performs first-person rendering; that is, it renders the world as seen from your eyes as you move about. The rate of @@ -599,7 +599,7 @@ any tilted walls. For simplicity's sake, I have chosen not to implement this in Listing 62.1, but you may find it educational to add it to the program yourself. -### Transformation into Viewspace {#Heading6} +### Transformation into Viewspace The viewing angle (which controls direction of movement as well as view direction) can sweep through the full 360 degrees around the viewpoint, @@ -626,7 +626,7 @@ values result in narrower fields of view. When this is done the walls are in viewspace, ready to be clipped. -### Clipping {#Heading7} +### Clipping In viewspace, the walls may be anywhere relative to the viewpoint: in front, behind, off to the side. We only want to draw those parts of @@ -675,7 +675,7 @@ near clip plane guarantees that no remaining polygon point can have z\<=0, ensuring that when we project we'll always pass valid, y-clippable screenspace vertices to the polygon filler. -### Projection to Screenspace {#Heading8} +### Projection to Screenspace At this point, we have viewspace vertices for each wall that's at least partially visible. All we have to do is project these vertices according @@ -686,7 +686,7 @@ as the last step for visible walls in `ClipWalls()`. ![**Figure 62.3**  *Why y clipping is more complex than x or z clipping.*](images/62-03.jpg) -### Walking the Tree, Backface Culling and Drawing {#Heading9} +### Walking the Tree, Backface Culling and Drawing Now that we have all the walls clipped to the frustum, with vertices projected into screen coordinates, all we have to do is draw them back @@ -744,7 +744,7 @@ the new frame to the screen. The frame of animation is complete. ![**Figure 62.4**  *Fast backspace culling test in screenspace.*](images/62-04.jpg) -#### Notes on the BSP Renderer {#Heading10} +#### Notes on the BSP Renderer Listing 62.1 is far from complete or optimal. There is no such thing as a tiny BSP rendering demo, because 3D rendering, even when based on a diff --git a/chapter-63.md b/chapter-63.md index e261357..7d99e8a 100644 --- a/chapter-63.md +++ b/chapter-63.md @@ -11,9 +11,9 @@ pages: 1163-1175 --- ## Chapter 63\ - Floating-Point for Real-Time 3-D {#Heading1} + Floating-Point for Real-Time 3-D -### Knowing When to Hurl Conventional Math Wisdom Out the Window {#Heading2} +### Knowing When to Hurl Conventional Math Wisdom Out the Window In a crisis, sometimes it's best to go with the first solution that comes into your head—but not very often. @@ -94,7 +94,7 @@ quickly assumptions that once were completely valid can deteriorate. For example, consider floating-point math. -### Not Your Father's Floating-Point {#Heading3} +### Not Your Father's Floating-Point Until last year, I had never done any serious floating-point (FP) optimization, for the perfectly good reason that FP math had never been @@ -132,7 +132,7 @@ 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} +### Pentium Floating-Point Optimization 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 @@ -188,7 +188,7 @@ instructions starts. There's a more exciting possibility here, though: Given properly structured code, the FPU is capable of averaging 1 cycle per FADD, FSUB, or FMUL. The secret is pipelining. -#### Pipelining, Latency, and Throughput {#Heading5} +#### Pipelining, Latency, and Throughput The Pentium's FPU is the first pipelined x86 FPU. *Pipelining* means that the FPU is capable of starting an instruction every cycle, and can @@ -258,7 +258,7 @@ two instructions. When dependencies like this occur, the FPU runs at latency rather than throughput speeds, and performance can drop by as much as two-thirds. -#### FXCH {#Heading6} +#### FXCH One piece of the puzzle is still missing. Clearly, to get maximum throughput, we need to interleave FP instructions, such that at any one @@ -297,7 +297,7 @@ multiplications, without incurring any stalls, as shown in Listing 63.1. faddp st(2),st(0) ;starts on cycle 6 ``` -### The Dot Product {#Heading7} +### The Dot Product Now we're ready to look at fast FP for common 3-D operations; we'll start by looking at how to speed up the dot product. As discussed in @@ -355,7 +355,7 @@ potential, as we'll see when we discuss transformation. ; ends on cycle 14 ``` -### The Cross Product {#Heading8} +### The Cross Product When last we looked at the cross product, we found that it's handy for generating a vector that's normal to two other vectors. The cross @@ -442,7 +442,7 @@ of properly managing the Pentium's FP pipeline. ; ends on cycle 21 ``` -### Transformation {#Heading9} +### Transformation Transforming a point, for example from worldspace to viewspace, is one of the most heavily used FP operations in realtime 3-D. Conceptually, @@ -528,7 +528,7 @@ certainly feasible; at a frame rate of 30 Hz, that's an impressive ; ends on cycle 33 ``` -### Projection {#Heading10} +### Projection The final optimization we'll look at is projection to screenspace. Projection itself is basically nothing more than a divide (to get 1/z), @@ -554,7 +554,7 @@ precision-related problems, such as clipped values that vary more than you'd expect from the precise clip point, or the need for using larger epsilons in comparisons for point-on-plane tests. -### Rounding Control {#Heading11} +### Rounding Control Another useful area that I can note only in passing here is that of leaving the FPU in a particular rounding mode while performing bulk @@ -576,7 +576,7 @@ A final note: There are some speed-ups to be had by manipulating FP variables with integer instructions. Check out Chris Hecker's column in the February/March 1996 issue of *Game Developer* for details. -### A Farewell to 3-D Fixed-Point {#Heading12} +### A Farewell to 3-D Fixed-Point As with most optimizations, there are both benefits and hazards to floating-point acceleration, especially pedal-to-the-metal optimizations diff --git a/chapter-64.md b/chapter-64.md index 2b7b152..58cf660 100644 --- a/chapter-64.md +++ b/chapter-64.md @@ -11,9 +11,9 @@ pages: 1177-1190 --- ## Chapter 64\ - Quake's Visible-Surface Determination {#Heading1} + Quake's Visible-Surface Determination -### The Challenge of Separating All Things Seen from All Things Unseen {#Heading2} +### The Challenge of Separating All Things Seen from All Things Unseen Years ago, I was working at Video Seven, a now-vanished video adapter manufacturer, helping to develop a VGA clone. The fellow who was @@ -83,7 +83,7 @@ seemingly outlandish ideas can open up new design possibilities for you. Case in point: The evolution of Quake's 3-D graphics engine. -### VSD: The Toughest 3-D Challenge of All {#Heading3} +### VSD: The Toughest 3-D Challenge of All I've spent most of my waking hours for the last several months working on Quake, id Software's successor to DOOM, and I suspect I have a few @@ -119,7 +119,7 @@ detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level. -### The Structure of Quake Levels {#Heading4} +### The Structure of Quake Levels Before diving into VSD, let me note that each Quake level is stored as a single huge 3-D BSP tree. This BSP tree, like any BSP, subdivides space, @@ -135,7 +135,7 @@ the boundaries of the BSP leaves, facing inward, the polygons in a given leaf can never obscure one another and can be drawn in any order. (This is a general property of convex polyhedra.) -### Culling and Visible Surface Determination {#Heading5} +### Culling and Visible Surface Determination The process of VSD would ideally work as follows: First, you would cull all polygons that are completely outside the view frustum (view @@ -172,7 +172,7 @@ the world, more transformations and tests have to be performed to cull polygons that aren't visible; at some point, that will bog considerably performance down. -#### Nodes Inside and Outside the View Frustum {#Heading6} +#### Nodes Inside and Outside the View Frustum Happily, there's a good workaround for this particular problem. As discussed earlier, each leaf of a BSP tree represents a convex subspace, @@ -199,7 +199,7 @@ specifically for culling tests. So culling to the frustum isn't a problem, and the BSP can be used to draw back-to- front. What, then, *is* the problem? -### Overdraw {#Heading7} +### Overdraw The problem John Carmack, the driving technical force behind DOOM and Quake, faced when he designed Quake was that in a complex world, many @@ -241,7 +241,7 @@ By three months after I arrived, only one element of the original VSD design was anywhere in sight, and John had taken the dictum of "try new things" farther than I'd ever seen it taken. -### The Beam Tree {#Heading8} +### The Beam Tree John's original Quake design was to draw front-to-back, using a second BSP tree to keep track of what parts of the screen were already drawn @@ -294,7 +294,7 @@ proved to suffer from much the same malady as the painter's algorithm: The worst case was much worse than the average case, and it didn't scale well with increasing level complexity. -### 3-D Engine du Jour {#Heading9} +### 3-D Engine du Jour Once the beam tree was working, John relentlessly worked at speeding up the 3-D engine, always trying to improve the design, rather than @@ -311,7 +311,7 @@ are some of those approaches, presented in minimal detail in the hopes that, like Tom Wilson with the Paradise FIFO, your imagination will be sparked. -#### Subdividing Raycast {#Heading10} +#### Subdividing Raycast Rays are cast in an 8x8 screen-pixel grid; this is a highly efficient operation because the first intersection with a surface can be found by @@ -324,7 +324,7 @@ This scales very well, being limited by the number of pixels, with no overdraw. The problem is dropouts; it's quite possible for small polygons to fall between rays and vanish. -#### Vertex-Free Surfaces {#Heading11} +#### Vertex-Free Surfaces The world is represented by a set of surface planes. The polygons are implicit in the plane intersections, and are extracted from the planes @@ -332,7 +332,7 @@ as a final step before drawing. This makes for fast clipping and a very small data set (planes are far more compact than polygons), but it's time-consuming to extract polygons from planes. -#### The Draw-Buffer {#Heading12} +#### The Draw-Buffer Like a z-buffer, but with 1 bit per pixel, indicating whether the pixel has been drawn yet. This eliminates overdraw, but at the cost of an @@ -344,7 +344,7 @@ for drawing 0-8 pixels, in the process possibly taking advantage of the ability of the x86 to do the perspective floating-point divide in parallel while 8 pixels are processed. -#### Span-Based Drawing {#Heading13} +#### Span-Based Drawing Polygons are rasterized into spans, which are added to a global span list and clipped against that list so that only the nearest span at each @@ -353,7 +353,7 @@ because if there's any overlap, the span already in the list is nearer. This eliminates overdraw, but at the cost of a lot of span arithmetic; also, every polygon still has to be turned into spans. -#### Portals {#Heading14} +#### Portals The holes where polygons are missing on surfaces are tracked, because it's only through such portals that line-of-sight can extend. Drawing @@ -363,7 +363,7 @@ portals remain visible. Applied recursively, this allows drawing only the visible portions of visible polygons, but at the cost of a considerable amount of portal clipping. -### Breakthrough! {#Heading15} +### Breakthrough! In the end, John decided that the beam tree was a sort of second-order structure, reflecting information already implicitly contained in the @@ -421,7 +421,7 @@ still-in-development sorted-edge rasterizer that completely eliminates overdraw, comes remarkably close to meeting the "perfect-world" specifications we laid out at the start. -### Simplify, and Keep on Trying New Things {#Heading16} +### Simplify, and Keep on Trying New Things What does it all mean? Exactly what I said up front: Simplify, and keep trying new things. The precalculated PVS is simpler than any of the @@ -463,7 +463,7 @@ things better with less code. So far, it seems to have worked out pretty well for him. -### Learn Now, Pay Forward {#Heading17} +### Learn Now, Pay Forward There's one other thing I'd like to mention before I close this chapter. Much of what I've learned, and a great deal of what I've written, has @@ -494,7 +494,7 @@ in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future! -### References {#Heading18} +### References Foley, James D., *et al.*, *Computer Graphics: Principles and Practice*, Addison Wesley, 1990, ISBN 0-201-12110-7 (beams, BSP trees, VSD). diff --git a/chapter-65.md b/chapter-65.md index a1476ea..836a3fc 100644 --- a/chapter-65.md +++ b/chapter-65.md @@ -11,9 +11,9 @@ pages: 1191-1208 --- ## Chapter 65\ - 3-D Clipping and Other Thoughts {#Heading1} + 3-D Clipping and Other Thoughts -### Determining What's Inside Your Field of View {#Heading2} +### Determining What's Inside Your Field of View Our part of the world is changing, and I'm concerned. By way of explanation, three anecdotes. @@ -100,7 +100,7 @@ circulated a good bit of info about 3-D graphics, and plan to keep on doing it as long as I can. Next, we're going to take a look at 3-D clipping. -### 3-D Clipping Basics {#Heading3} +### 3-D Clipping Basics Before I got deeply into 3-D, I kept hearing how difficult 3-D clipping was, so I was pleasantly surprised when I actually got around to doing @@ -137,7 +137,7 @@ ways to speed up clipping under various circumstances, some of which I'll mention, but the material covered below will give you the tools you need to implement functional 3-D clipping. -#### Intersecting a Line Segment with a Plane {#Heading4} +#### Intersecting a Line Segment with a Plane The fundamental 3-D clipping operation is clipping a line segment to a plane. There are two parts to this operation: determining if the line is @@ -246,7 +246,7 @@ y, and z lengths of the line segment by that fraction, and add the results to the inside endpoint, we get a new, clipped endpoint at the point of intersection. -### Polygon Clipping {#Heading5} +### Polygon Clipping Line clipping is fine for wireframe rendering, but what we really want to do is polygon rendering of solid models, which requires polygon @@ -345,7 +345,7 @@ intersection with the clip plane can be used to advance the texture coordinates as well, so only one extra multiply and one extra add are required for each texture coordinate. -#### Clipping to the Frustum {#Heading6} +#### Clipping to the Frustum Given a polygon-clipping function, it's easy to clip to the frustum: set up the four planes for the sides of the frustum, with another one or two @@ -669,7 +669,7 @@ void UpdateWorld() } ``` -#### The Lessons of Listing 65.3 {#Heading7} +#### The Lessons of Listing 65.3 There are several interesting points to Listing 65.3. First, floating-point arithmetic is used throughout the clipping process. While @@ -731,7 +731,7 @@ viewspace, then clipping them. However, the decision whether to clip in worldspace or viewspace is not clear-cut and is affected by several factors. -### Advantages of Viewspace Clipping {#Heading8} +### Advantages of Viewspace Clipping Although viewspace clipping requires transforming vertices that may not be drawn, it has potential performance advantages. For example, in @@ -771,7 +771,7 @@ the more frustum clipping you're doing, especially if most of the polygons are trivially visible, the more attractive the performance advantages of normalized clipping become. -### Further Reading {#Heading9} +### Further Reading You now have the basics of 3-D clipping, but because fast clipping is central to high-performance 3-D, there's a lot more to be learned. One diff --git a/chapter-66.md b/chapter-66.md index 48e82e3..fc18a9b 100644 --- a/chapter-66.md +++ b/chapter-66.md @@ -11,9 +11,9 @@ pages: 1209-1222 --- ## Chapter 66\ - Quake's Hidden-Surface Removal {#Heading1} + Quake's Hidden-Surface Removal -### Struggling with Z-Order Solutions to the Hidden Surface Problem {#Heading2} +### Struggling with Z-Order Solutions to the Hidden Surface Problem Okay, I admit it: I'm sick and tired of classic rock. Admittedly, it's been a while, about 20 years, since I was last excited to hear anything @@ -55,7 +55,7 @@ I've found that they're often worth considering. Not that I should have needed any reminding, considering the ever-evolving nature of Quake. -### Creative Flux and Hidden Surfaces {#Heading3} +### Creative Flux and Hidden Surfaces Back in Chapter 64, I described the creative flux that led to John Carmack's decision to use a precalculated potentially visible set (PVS) @@ -70,7 +70,7 @@ drawing perform the final stage of hidden-surface removal (HSR). This was a terrific idea, but it was far from the end of the road for Quake's design. -#### Drawing Moving Objects {#Heading4} +#### Drawing Moving Objects For one thing, there was still the question of how to sort and draw moving objects properly; in fact, this is the single technical question @@ -102,7 +102,7 @@ drawing and z-filling of the world is done, we can simply draw the sprites and polygon models with z-buffering and get perfect sorting all around. -#### Performance Impact {#Heading5} +#### Performance Impact Whenever a z-buffer is involved, the questions inevitably are: What's the memory footprint and what's the performance impact? Well, the memory @@ -117,7 +117,7 @@ vastly improved the visual quality and flexibility of the Quake engine, and also simplified the code quite a bit, at an acceptable memory and performance cost. -#### Leveling and Improving Performance {#Heading6} +#### Leveling and Improving Performance As I said above, in the Quake architecture, the world itself is drawn first, without z-buffer reads or compares, but filling the z-buffer with @@ -158,7 +158,7 @@ than back-to-front drawing. And indeed there is. -### Sorted Spans {#Heading7} +### Sorted Spans The ideal final HSR stage for Quake would reject all the polygons in the PVS that are actually invisible, and draw only the visible pixels of the @@ -214,7 +214,7 @@ implement, with a couple of major design choices to be made, a subtle mathematical element, and some tricky gotchas that I'll have to defer until Chapter 67. Let's look at the design choices first. -### Edges versus Spans {#Heading8} +### Edges versus Spans The first design choice is whether to sort spans or edges (both of which fall into the general category of "sorted spans"). Although the results @@ -314,7 +314,7 @@ process, I'm going to have to make a few forward references to aspects of edge-sorting that I haven't yet covered in detail; my apologies, but it's unavoidable, and all should become clear by the end of Chapter 67. -### Edge-Sorting Keys {#Heading9} +### Edge-Sorting Keys Now that we know we're going to sort edges, using them to emit spans for the polygons nearest the viewer, the question becomes: How can we tell @@ -389,7 +389,7 @@ floating-point math sounds expensive but really isn't, especially on a Pentium, where a plane's 1/z value at any point can be calculated in as little as six cycles in assembly language. -#### Where That 1/Z Equation Comes From {#Heading10} +#### Where That 1/Z Equation Comes From For those who are interested, here's a quick derivation of the 1/z equation. The plane equation for a plane is @@ -410,7 +410,7 @@ Inverting and distributing yields: We'll see 1/z sorting in action in Chapter 67. -#### Quake and Z-Sorting {#Heading11} +#### Quake and Z-Sorting I mentioned earlier that Quake no longer uses BSP order as the sorting key; in fact, it uses 1/z as the key now. Elegant as the gradients are, @@ -438,7 +438,7 @@ so they don't cross any solid world surfaces, to avoid complications associated with interpenetration), along with all the world edges, and 1/z sorting takes care of the rest. -### Decisions Deferred {#Heading12} +### Decisions Deferred There is, without a doubt, an awful lot of information in the preceding pages, and it may not all connect together yet in your mind. The code diff --git a/chapter-67.md b/chapter-67.md index 5e488fe..c4b4770 100644 --- a/chapter-67.md +++ b/chapter-67.md @@ -11,9 +11,9 @@ pages: 1223-1241 --- ## Chapter 67\ - Sorted Spans in Action {#Heading1} + Sorted Spans in Action -### Implementing Independent Span Sorting for Rendering without Overdraw {#Heading2} +### Implementing Independent Span Sorting for Rendering without Overdraw In Chapter 66, we dove headlong into the intricacies of hidden surface removal by way of z-sorted (actually, 1/z-sorted) spans. At the end of @@ -49,7 +49,7 @@ contained herein. Besides, the ultimate reference for any design is working code, which you'll find, in part, in Listing 67.1, and in its entirety in the file DDJZSORT.ZIP on the CD-ROM. -### Quake and Sorted Spans {#Heading3} +### Quake and Sorted Spans As you'll recall from Chapter 66, Quake uses sorted spans to get zero overdraw while rendering the world, thereby both improving overall @@ -152,7 +152,7 @@ For the remainder of this chapter, I'm going to look at the three main types of 1/z span sorting, then discuss a sample 3-D app built around 1/z span sorting. -### Types of 1/z Span Sorting {#Heading4} +### Types of 1/z Span Sorting As a quick refresher: With 1/z span sorting, all the polygons in a scene are treated as sets of screenspace pixel spans, and 1/z (where z is @@ -168,7 +168,7 @@ they are: intersecting, abutting, and independent. (These are names of my own devising; I haven't come across any standard nomenclature in the literature.) -#### Intersecting Span Sorting {#Heading5} +#### Intersecting Span Sorting Intersecting span sorting occurs when polygons can interpenetrate. Thus, two spans may cross such that part of each span is visible, in which @@ -183,7 +183,7 @@ detect interpenetration, and additional work must be done to split the spans as necessary. Thus, although intersecting span sorting certainly works, it's not the first choice for performance. -#### Abutting Span Sorting {#Heading6} +#### Abutting Span Sorting Abutting span sorting occurs when polygons that are not part of a continuous surface can butt up against one another, but don't @@ -238,7 +238,7 @@ of caching sort results were outweighed by the additional overhead of maintaining the caching information, and every caching variant we tried actually slowed Quake down. -#### Independent Span Sorting {#Heading7} +#### Independent Span Sorting Finally, we come to independent span sorting, the simplest and fastest of the three, and the type the sample code in Listing 67.1 uses. Here, @@ -253,7 +253,7 @@ Independent span sorting is ideal for scenes with lots of moving objects that never actually touch each other, such as a space battle. Next, we'll look at an implementation of independent 1/z span sorting. -### 1/z Span Sorting in Action {#Heading8} +### 1/z Span Sorting in Action Listing 67.1 is a portion of a program that demonstrates independent 1/z span sorting. This program is based on the sample 3-D clipping program @@ -788,7 +788,7 @@ necessary. As you can see from Listing 67.1, it takes a fair bit of code to implement this, but all that's really going on is a surface stack driven by edge events. -#### Implementation Notes {#Heading9} +#### Implementation Notes Finally, a few notes on Listing 67.1. First, you'll notice that although we clip all polygons to the view frustum in worldspace, we nonetheless diff --git a/chapter-68.md b/chapter-68.md index 14ae39c..32280b0 100644 --- a/chapter-68.md +++ b/chapter-68.md @@ -11,9 +11,9 @@ pages: 1243-1256 --- ## Chapter 68\ - Quake's Lighting Model {#Heading1} + Quake's Lighting Model -### A Radically Different Approach to Lighting Polygons {#Heading2} +### A Radically Different Approach to Lighting Polygons It was during my senior year in college that I discovered computer games. Not Wizardry, or Choplifter, or Ultima, because none of those @@ -65,7 +65,7 @@ doing, and if it's getting stale, it's time to learn something new; there's plenty of interesting programming of all sorts to be done. Follow your interests—and don't forget to have fun! -### The Lighting Conundrum {#Heading3} +### The Lighting Conundrum I spent about two years working with John Carmack on Quake's 3-D graphics engine. John faced several fundamental design issues while @@ -89,7 +89,7 @@ and rock-solid, complex lighting proved to be difficult to achieve with traditional lighting approaches; ultimately, a dramatically different approach was required. -### Gouraud Shading {#Heading4} +### Gouraud Shading The traditional way to do realistic lighting in polygon pipelines is Gouraud shading (also known as *smooth shading*). Gouraud shading @@ -118,7 +118,7 @@ small amount of calculation and a compact data set that's a simple extension of the basic polygon model. However, there are several important drawbacks to Gouraud shading, as well. -#### Problems with Gouraud Shading {#Heading5} +#### Problems with Gouraud Shading The quality of Gouraud shading depends heavily on the average size of the polygons being drawn. Linear interpolation is used, so highlights @@ -157,7 +157,7 @@ that increases the rasterization load. ![**Figure 68.1**  *Adding an extra vertex directly beneath a light.*](images/68-01.jpg) -#### Perspective Correctness {#Heading6} +#### Perspective Correctness Another problem is that Gouraud shading isn't perspective-correct. With Gouraud shading, lighting varies linearly across the face of a polygon, @@ -229,7 +229,7 @@ not only would the world still be less than totally solid, because of the limitations of Gouraud shading, but the engine would also be too slow to support the complex worlds we had hoped for in Quake. -### The Quest for Alternative Lighting {#Heading7} +### The Quest for Alternative Lighting None of which is to say that Gouraud shading isn't useful in general. Descent uses it to excellent effect, and in fact Quake uses Gouraud @@ -251,7 +251,7 @@ possibilities and continued working with Gouraud shading for lack of a better alternative—until the day John came into work and said, "You know, I have an idea...." -#### Decoupling Lighting from Rasterization {#Heading8} +#### Decoupling Lighting from Rasterization John's idea came to him while was looking at a wall that had been carved into several pieces because of a spotlight, with an ugly lighting glitch @@ -303,7 +303,7 @@ because lighting is unrelated to vertices. In short, surface-based lighting meets all of Quake's visual quality goals, which leaves only one question: How does it perform? -#### Size and Speed {#Heading9} +#### Size and Speed As it turns out, the raw speed of surface-based lighting is pretty good. Although an extra step is required to build the surface, moving lighting @@ -357,7 +357,7 @@ the combination of surface building and unlit texture mapping a potential performance problem, but that never posed a problem during the development of Quake, thanks to surface caching. -### Surface Caching {#Heading10} +### Surface Caching When he thought of surface-based lighting, John immediately realized that surface building would be relatively expensive. (In fact, he @@ -392,7 +392,7 @@ cache initially looked to be very large, on the order of several megabytes, even at 320x200—too much for a game intended to run on an 8 MB machine. -#### Mipmapping To The Rescue {#Heading11} +#### Mipmapping To The Rescue Two factors combined to solve this problem. First, polygons are drawn through an edge list with no overdraw, as I discussed a few chapters @@ -433,7 +433,7 @@ of texels, all at the mipmap level of the nearest vertex, and would require huge amounts of surface cache space while displaying a great deal of aliasing in distant regions due to a high texel:pixel ratio. -#### Two Final Notes on Surface Caching {#Heading12} +#### Two Final Notes on Surface Caching Dynamic lighting has a significant impact on the performance of surface caching, because whenever the lighting on a surface changes, the surface diff --git a/chapter-69.md b/chapter-69.md index 736c660..dd476a7 100644 --- a/chapter-69.md +++ b/chapter-69.md @@ -11,9 +11,9 @@ pages: 1257-1271 --- ## Chapter 69\ - Surface Caching and Quake's Triangle Models {#Heading1} + Surface Caching and Quake's Triangle Models -### Probing Hardware-Assisted Surfaces and Fast Model Animation Without Sprites {#Heading2} +### Probing Hardware-Assisted Surfaces and Fast Model Animation Without Sprites In the late '70s, I spent a summer doing contract programming at a government-funded installation called the Northeast Solar Energy Center @@ -83,7 +83,7 @@ and the more information we have, the better. In that spirit, let's look at more of the stuff that makes Quake tick, starting with what I've recently learned about surface caching. -### Surface Caching with Hardware Assistance {#Heading3} +### Surface Caching with Hardware Assistance In Chapter 68, I discussed in detail the surface caching technique that Quake uses to do detailed, high-quality lighting without lots of @@ -135,7 +135,7 @@ the case in software. Second, there are at least two alternatives that preserve the advantages of surface caching without many of the disadvantages noted above. -#### Letting the Graphics Card Build the Textures {#Heading4} +#### Letting the Graphics Card Build the Textures One obvious solution is to have the accelerator card build the textures, rather than having the CPU build and then download them. This eliminates @@ -157,7 +157,7 @@ as it stores them in texture memory. Better yet, some accelerators support 8-bpp palettized hardware textures that are expanded to 16-bpp on the fly during texturing.) -#### The Light Map as Alpha Texture {#Heading5} +#### The Light Map as Alpha Texture Another appealing non-caching approach is doing unlit texture-mapping in one pass, then lighting from the light map as a second pass, using the @@ -193,7 +193,7 @@ heavily toward hardware accelerators, and at this point it's a tossup whether the engine will use surface caching, Gouraud shading, or two-pass lighting. -### Drawing Triangle Models {#Heading6} +### Drawing Triangle Models Most of the last group of chapters in this book discuss how Quake works. If you look closely, though, you'll see that almost all of the @@ -212,7 +212,7 @@ discuss some interesting aspects of our triangle-model architecture, and present code for one useful approach for the rapid drawing of triangle models. -#### Drawing Triangle Models Fast {#Heading7} +#### Drawing Triangle Models Fast We would have liked one rendering model, and hence one graphics pipeline, for all drawing in Quake; this would have simplified the code @@ -256,7 +256,7 @@ between the vertices that suffer slight warping. ![**Figure 69.1**  *Quake's triangle-model drawing pipeline.*](images/69-01.jpg) -#### Trading Subpixel Precision for Speed {#Heading8} +#### Trading Subpixel Precision for Speed Another sacrifice at the altar of performance was subpixel precision. Before each triangle is drawn, we snap its vertices to the nearest @@ -290,7 +290,7 @@ so the model is always lit from the same direction. Somewhat surprisingly, in practice this looks considerably better than pure ambient lighting. -#### An Idea that Didn't Work {#Heading9} +#### An Idea that Didn't Work As we implemented triangle models, we tried several ideas that didn't work out. One that's notable because it seems so appealing is caching a @@ -328,7 +328,7 @@ if necessary, but the sprite architecture just had the feeling of being fundamentally not the right approach, so we tried thinking along different lines. -#### An Idea that Did Work {#Heading10} +#### An Idea that Did Work John Carmack had the notion that it was just way too much effort per pixel to do all the work of scanning out the tiny triangles in distant @@ -519,7 +519,7 @@ D_PolysetRecursiveTriangle (lp3, new, lp2); ![**Figure 69.2**  *One recursive subdivision triangle-drawing step.*](images/69-02.jpg) -#### More Ideas that Might Work {#Heading11} +#### More Ideas that Might Work Useful as subdivision rasterization proved to be, we by no means think that we've maxed out triangle-model drawing, if only because we spent diff --git a/chapter-70.md b/chapter-70.md index 0dc8e45..bea2eaa 100644 --- a/chapter-70.md +++ b/chapter-70.md @@ -11,7 +11,7 @@ pages: 1273-1285 --- ## Chapter 70\ - Quake: A Post-Mortem and a Glimpse into the Future {#Heading1} + Quake: A Post-Mortem and a Glimpse into the Future *Why did not any of the children in the first group think of this faster method of going across the room? It is simple. They looked at what they @@ -81,7 +81,7 @@ like Knuth, Foley and van Dam, Jim Blinn, Jim Kajiya, and hundreds of others—are you ready to take a shot at making your own contribution to the future? -### Preprocessing the World {#Heading2} +### Preprocessing the World For the most part, I'll discuss Quake's 3-D engine in this chapter, although I'll touch on other areas of interest. For 3-D rendering @@ -146,7 +146,7 @@ player can move, is completely surrounded by a solid region. This eliminates a great many irrelevant polygons, and reduces the complexity of the next step, calculating the potentially visible set. -### The Potentially Visible Set (PVS) {#Heading3} +### The Potentially Visible Set (PVS) After the BSP tree is built, the potentially visible set (PVS) for each leaf is calculated. The PVS for a leaf consists of all the leaves that @@ -229,7 +229,7 @@ radiosity lighting—a considerably more expensive process, but one that produces highly realistic lighting—is performed, but I'll save that for later. -### Passages: The Last-Minute Change that Didn't Happen {#Heading4} +### Passages: The Last-Minute Change that Didn't Happen Earlier, I mentioned that we almost changed 3-D engines again in the last month of Quake's development. Here's what happened: One of the @@ -287,7 +287,7 @@ future engine. The more approaches you try, the larger your toolkit and the broader your understanding will be when you tackle your next project. -### Drawing the World {#Heading5} +### Drawing the World Everything described so far is a preprocessing step. When Quake is actually running, the world is drawn as follows: First, the PVS for the @@ -361,7 +361,7 @@ Quake engine, but remains an inelegant solution, and, in the end, it feels like there's something better we didn't hit on. However, as John says, "I'm pragmatic above all else"—and the edge list did the job. -### Rasterization {#Heading6} +### Rasterization Once the visible spans are scanned out of the edge list, they must still be drawn, with perspective-correct texture mapping and lighting. This @@ -383,7 +383,7 @@ of 1/z is overlapped with drawing 16 pixels, taking advantage of the Pentium's ability to perform floating-point in parallel with integer instructions, so the FDIV effectively takes only one cycle. -#### Lighting {#Heading7} +#### Lighting Lighting is less simple to explain. The traditional way of doing polygon lighting is to calculate the correct light at the vertices and linearly @@ -422,7 +422,7 @@ splattered on a wall could be handled by drawing the splatter image as a sprite into the appropriate surface buffer, so that drawing the surface would draw the splatter as well. -#### Dynamic Lighting {#Heading8} +#### Dynamic Lighting Here we come to a feature added to Quake after last year's Computer Game Developer's Conference (CGDC). At that time, Quake did not support @@ -469,7 +469,7 @@ visually very solid and stable. This was an important design goal from the start, both as a point of technical pride and because it greatly improves the player's sense of immersion. -### Entities {#Heading9} +### Entities So far, all we've drawn is the static, unchanging (apart from dynamic lighting) world. That's an important foundation, but it's certainly not @@ -477,7 +477,7 @@ a game; now we need to add moving objects. These objects fall into four very different categories: BSP models, polygon models, sprites, and particles. -#### BSP Models {#Heading10} +#### BSP Models BSP models are just like the world, except that they can move. Examples include doors, moving bridges, and health and ammo boxes. The way these @@ -518,7 +518,7 @@ if the door opens). This makes BSP models most suitable for fairly simple structures, such as boxes, which have relatively few polygons to clip, and cause relatively few edges to be added to the edge list. -#### Polygon Models and Z-Buffering {#Heading11} +#### Polygon Models and Z-Buffering Polygon models, such as monsters, weapons, and projectiles, consist of a triangle mesh with front and back skins stretched over the model. For @@ -592,7 +592,7 @@ could be sent through a special fast path. The biggest breakthrough, though, was a very different sort of rasterizer that John came up with for relatively distant models. -#### The Subdivision Rasterizer {#Heading12} +#### The Subdivision Rasterizer This rasterizer, which we call the *subdivision rasterizer*, first draws all the vertices in the model. Then it takes each front-facing triangle, @@ -621,7 +621,7 @@ faster ways yet to rasterize distant models adequately well, but the subdivider was clearly a win, and is a good example of how thinking in a radically different direction can pay off handsomely. -#### Sprites {#Heading13} +#### Sprites We had hoped to be able to eliminate sprites completely, making Quake 100% 3-D, but sprites—although sometimes very visibly 2-D—were used for @@ -637,7 +637,7 @@ similar to drawing a normal polygon, complete with perspective correction, although of course the inner loop must detect and skip over transparent pixels, and must also perform z-buffering. -#### Particles {#Heading14} +#### Particles The last drawing entity type is particles. Each particle is a solid-colored rectangle, scaled by distance from the viewer and drawn @@ -650,7 +650,7 @@ example, providing a trail of fire behind a polygon-model lava ball that flies into the air, or generating an expanding cloud around a sprite explosion core. -### How We Spent Our Summer Vacation: After Shipping Quake {#Heading15} +### How We Spent Our Summer Vacation: After Shipping Quake Since shipping Quake in the summer of 1996, we've extended it in several ways: We've worked with Rendition to port it to the Verite accelerator @@ -658,7 +658,7 @@ chip, we've ported it to OpenGL, we've ported it to Win32, we've done QuakeWorld, and we've added features for Quake 2. I'll discuss each of these briefly. -#### Verite Quake {#Heading16} +#### Verite Quake Verite Quake (VQuake) was the first hardware-accelerated version of Quake. It looks extremely good, due to bilinear texture filtering, which @@ -719,7 +719,7 @@ it seems most likely that the two approaches will be mixed together, with surface caching used for special surfaces, and two-pass alpha lighting used for most drawing. -#### GLQuake {#Heading17} +#### GLQuake The second (and, according to current plans, last) port of Quake to a hardware accelerator was an OpenGL version, GLQuake, a native Win32 @@ -809,7 +809,7 @@ are standard equipment on accelerators, and it's a lot of fun seeing what sorts of previously very difficult effects can now be up and working in a matter of hours. -#### WinQuake {#Heading18} +#### WinQuake I'm not going to spend much time on the Win32 port of Quake; most of what I learned doing this consists of tedious details that are doubtless @@ -833,7 +833,7 @@ Still, when you get down to it, the future of gaming is a networked Win32 world, and that's that, so if you haven't already moved to Win32, I'd say it's time. -#### QuakeWorld {#Heading19} +#### QuakeWorld QuakeWorld is a native Win32 multiplayer-only version of Quake, and was done as a learning experience; it is not a commercial product, but is @@ -941,7 +941,7 @@ tradeoff of smoothness and perceived low latency for the frustration of paradoxes—and that's the way it's going to stay until most people are connected to the Internet by something better than modems. -#### Quake 2 {#Heading20} +#### Quake 2 I can't talk in detail about Quake 2 as a game, but I can describe some interesting technology features. The Quake 2 rendering engine isn't @@ -1000,7 +1000,7 @@ considers this the game interface of the future. By the way, Quake 2 is currently being developed as a native Win32 app only; no DOS version is planned. -### Looking Forward {#Heading21} +### Looking Forward In my address to the Computer Game Developer's Conference in 1996, I said that it wasn't a bad time to start up a game company aimed at diff --git a/intro.md b/intro.md index ef60d6c..fdddb41 100644 --- a/intro.md +++ b/intro.md @@ -8,7 +8,7 @@ category: 'Web and Software Development: Game Development,Web and Software Devel Graphics and Multimedia Development' --- -# Introduction {#Heading1} +# Introduction What was it like working with John Carmack on Quake? Like being strapped onto a rocket during takeoff—in the middle of a hurricane. It seemed