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

61 lines
3.8 KiB
Markdown

Within Listing 57.2, all the important optimization is in the loop that
draws across each destination scan line, near the end of the listing.
One optimization is elimination of the call to the set-pixel routine
used to draw each pixel in Listing 57.1. Function calls are expensive
operations, to be avoided when performance matters. Also, although Mode
X (the undocumented 320x240 256-color VGA mode X-Sharp runs in) doesn't
lend itself well to pixel-oriented operations like line drawing or
texture mapping, the inner loop has been set up to minimize Mode X's
overhead. A rotating plane mask is maintained in AL, with DX pointing to
the Map Mask register; thus, only a rotate and an **OUT** are required
to select the plane to which to write, cycling from plane 0 through
plane 3 and wrapping back to 0. Better yet, because we know that we're
simply stepping horizontally across the destination scan line, we can
use a clever optimization to both step the destination and reduce the
overhead of maintaining the mask. Two copies of the current plane mask
are maintained, one in each nibble of AL. (The Map Mask register pays
attention only to the lower nibble.) Then, when one copy rotates out of
the lower nibble, the other copy rotates into the lower nibble and is
ready to be used. This approach eliminates the need to test for the mask
wrapping from plane 3 to plane 0, all the more so because a carry is
generated when wrapping occurs, and that carry can be added to DI to
advance the screen pointer. (Check out the next chapter, however, to see
the best Map Mask optimization of all—setting it once and leaving it
unchanged.)
In all, the overhead of drawing each pixel is reduced from a call to the
set-pixel routine and full calculation of the screen address and plane
mask to five instructions and no branches. This is an excellent example
of converting full, from-scratch calculations to incremental processing,
whereby only information that has changed since the last operation (the
plane mask moving one pixel, for example) is recalculated.
Incremental processing and knowing where the cycles go are both
important in the final optimization in Listing 57.2, speeding up the
retrieval of pixels from the texture map. This operation looks very
efficient in Listing 57.1, consisting of only two adds and the macro
**GET- IMAGE-PIXEL**. However, those adds are fixed-point adds, so they
take four instructions apiece, and the macro hides not only conversion
from fixed-point to integer, but also a time-consuming multiplication.
Incremental approaches are excellent at avoiding multiplication, because
cumulative additions can often replace multiplication. That's the case
with stepping through the source texture in Listing 57.2; ten
instructions, with a maximum of two branches, replace all the texture
calculations of Listing 57.1. Listing 57.2 simply detects when the
fractional part of the source x or y coordinate turns over and advances
the source texture pointer accordingly.
As you might expect, all this optimization is pretty hard to implement,
and makes Listing 57.2 much more complicated than Listing 57.1. Is it
worth the trouble? Indeed it is. Listing 57.2 is more than twice as fast
as Listing 57.1, and the difference is very noticeable when large,
texture-mapped areas are animated. Whether more than doubling
performance is significant is a matter of opinion, I suppose, but
imagine that you're in William Gibson's *Neuromancer*, trying to crack a
corporate database. Which texture-mapping routine would you rather have
interfacing you to Cyberspace?
I'm always interested in getting your feedback on and hearing about
potential improvements to X-Sharp. Contact me through the publisher.
There is no truth to the rumor that I can be reached under the alias
"sheep-shearer," at least not for another 9,999 sheep.