112 lines
11 KiB
HTML
112 lines
11 KiB
HTML
<HTML>
|
|
<HEAD>
|
|
<META name=vsisbn content="1576101746">
|
|
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
|
|
<META name=vsauthor content="Michael Abrash">
|
|
<META name=vspublisher content="The Coriolis Group">
|
|
<META name=vspubdate content="07/01/97">
|
|
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Heinlein's Crystal Ball, Spock's Brain, and the 9-Cycle Dare</TITLE>
|
|
|
|
<!-- HEADER -->
|
|
<!-- Empty Reference Subhead -->
|
|
|
|
<!--ISBN=1576101746//-->
|
|
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
|
|
<!--AUTHOR=Michael Abrash//-->
|
|
<!--PUBLISHER=The Coriolis Group, Inc.//-->
|
|
<!--CHAPTER=58//-->
|
|
<!--PAGES=1085-1089//-->
|
|
<!--UNASSIGNED1//-->
|
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
|
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="58-02.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="58-04.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
<P><BR></P>
|
|
<P>I’d like to emphasize that algorithmically and conceptually, there is <I>no</I> difference between scanning out a polygon top to bottom and scanning it out left to right; it is only in conjunction with the hardware organization of Mode X that the scanning direction matters in the least.</P>
|
|
<TABLE WIDTH="100%"><TR>
|
|
<TD VALIGN="TOP" ALIGN="LEFT" WIDTH="5%"><IMG SRC="images/58-01i.jpg"><TD VALIGN="TOP" ALIGN="LEFT" WIDTH="95%"><SMALL><I>That’s what Zen programming is all about, though; tying together two pieces of seemingly unrelated information to good effect—and that’s what I had failed to do. Like Robert Heinlein—like all of us—I had viewed the world through a filter composed of my ingrained assumptions, and one of those assumptions, based on all my past experience, was that pixel processing proceeds left to right. Eventually, I might have come up with Chris’s approach; but I would only have come up with it when and if I relaxed and stepped back a little, and allowed myself—almost dared myself—to think of it. When you’re optimizing, be sure to leave quiet, nondirected time in which to conjure up those less obvious solutions, and periodically try to figure out what assumptions you’re making—and then question them!</I></SMALL>
|
|
</TABLE>
|
|
<P><A NAME="Fig3"><!-- </A><A HREF="javascript:displayWindow('images/58-03.jpg',407,145 )"> --><IMG SRC="images/58-03.jpg"><BR><!-- </A>
|
|
<BR><A HREF="javascript:displayWindow('images/58-03.jpg',407,145)"> --><FONT COLOR="#000077"><B>Figure 58.3</B></FONT></A> <I>Texture mapping a single vertical column.</I>
|
|
</P>
|
|
<P>There are a few complications with Chris’s approach, not least that X-Sharp’s polygon-filling convention (top and left edges included, bottom and right edges excluded) is hard to reproduce for column-oriented texture mapping. I solved this in X-Sharp version 22 by tweaking the edge-scanning code to allow column-oriented texture mapping to match the current convention. (You’ll find X-Sharp 22 on the listings diskette in the directory for this chapter.)
|
|
</P>
|
|
<P>Chris also illustrated another important principle of optimization: A second pair of eyes is invaluable. Even the best of us have blind spots 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.</P>
|
|
<H3><A NAME="Heading6"></A><FONT COLOR="#000077">That’s Nice—But it Sure as Heck Ain’t 9 Cycles</FONT></H3>
|
|
<P>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. Traditionally, I start the optimization process with algorithmic optimization, then try to tie the algorithm and the hardware together for maximum efficiency, and finish up with instruction-by-instruction, take-no-prisoners optimization. We’ve already done the first two steps, so it’s time to get down to the bare metal.
|
|
</P>
|
|
<P>Listing 58.2 contains three functional parts: Drawing the pixel, advancing the destination pointer, and advancing the source texture pointer. Each of the three parts is amenable to further acceleration.</P>
|
|
<P>Drawing the pixel is difficult to speed up, given that it consists of only two instructions—difficult, but not impossible. True, the instructions themselves are indeed irreducible, but if we can get rid of the ES: prefix (and, as we shall see, we can), we can rearrange the code to make it run faster on the Pentium. Without a prefix, the instructions execute as follows on the Pentium:</P>
|
|
<!-- CODE SNIP //-->
|
|
<PRE>
|
|
|
|
MOV AH,[BX] ;cycle 1 U-pipe
|
|
;cycle 1 V-pipe idle; reg contention
|
|
MOV [DI],AH ;cycle 2 U-pipe
|
|
|
|
</PRE>
|
|
<!-- END CODE SNIP //-->
|
|
<P>The second <B>MOV</B>, being dependent on the value loaded into AH by the first <B>MOV</B>, can’t execute until the first <B>MOV</B> is finished, so the Pentium’s second pipe, the V-pipe, lies idle for a cycle. We can reclaim that cycle simply by shuffling another instruction between the two <B>MOV</B>s.</P>
|
|
<P>Advancing the destination pointer is easy to speed up: Just build the offset from one scanline to the next into each pixel-drawing instruction as a constant, as in</P>
|
|
<!-- CODE SNIP //-->
|
|
<PRE>
|
|
|
|
MOV [EDI+SCANOFFSET],AH
|
|
|
|
</PRE>
|
|
<!-- END CODE SNIP //-->
|
|
<P>and advance EDI only once per unrolled loop iteration.
|
|
</P>
|
|
<P>Advancing the source texture pointer is more complex, but correspondingly more rewarding. Listing 58.2 uses a variant form of 32-bit fixed-point arithmetic to advance the source pointer, with the source texture coordinates and increments stored in 16.16 (16 bits of integer, 16 bits of fraction) format. The source coordinates are stored in a slightly unusual format, whereby the fractional X and Y coordinates are stored and advanced separately, but a single integer value, the source pointer, is used to reflect both the X and Y coordinates. In Listing 58.2, the integer and fractional parts are added into the current coordinates with four separate 16-bit operations, and carries from fractional to integer parts are detected via conditional jumps, as shown in Figure 58.4. There’s quite a lot we can do to improve this.</P>
|
|
<P><A NAME="Fig4"><!-- </A><A HREF="javascript:displayWindow('images/58-04.jpg',407,396 )"> --><IMG SRC="images/58-04.jpg"><BR><!-- </A>
|
|
<BR><A HREF="javascript:displayWindow('images/58-04.jpg',407,396)"> --><FONT COLOR="#000077"><B>Figure 58.4</B></FONT></A> <I>Original method for advancing the source texture pointer.</I>
|
|
</P>
|
|
<P>First, we can sum the X and Y integer advance amounts outside the loop, then add them both to the source pointer with a single instruction. Second, we can recognize that X advances exactly one extra byte when its fractional part carries, and use <B>ADC</B> to account for X carries, as shown in Figure 58.5. That single <B>ADC</B> can add in not only any X carry, but both the X and Y integer advance amounts as well, thereby eliminating a good chunk of the source-advance code in Listing 58.2. Furthermore, we should somehow be able to use 32-bit registers and instructions to help with the 32-bit fixed-point arithmetic; true, the size override prefix (because we’re in a 16-bit segment) will cost a cycle per 32-bit instruction, but that’s better than the 3 cycles it takes to do 32-bit arithmetic with 16-bit instructions. It isn’t obvious, but there’s a nifty trick we can use here, again courtesy of Chris Hecker (who, as you can tell, has done a fair amount of thinking about the complexities of texture mapping).</P>
|
|
<P>We can store the current fractional parts of both the X <I>and</I> Y source coordinates in a single 32-bit register, EDX, as shown in Figure 58.6. It’s important to note that the Y fraction is actually only 15 bits, with bit 15 of EDX always kept at zero; this allows bit 15 to store the carry status from each Y advance. We can similarly store the fractional X and Y advance amounts in ECX, and can store the sum of the integer parts of the X and Y advance amounts in BP. With this arrangement, the single instruction <B>ADD EDX,ECX</B> advances the fractional parts of both X and Y, and the following instruction <B>ADC SI,BP</B> finishes advancing the source pointer in X. That’s a mere 3 cycles, and all that remains is to finish advancing the source pointer in Y.</P>
|
|
<P><A NAME="Fig5"><!-- </A><A HREF="javascript:displayWindow('images/58-05.jpg',406,252 )"> --><IMG SRC="images/58-05.jpg"><BR><!-- </A>
|
|
<BR><A HREF="javascript:displayWindow('images/58-05.jpg',406,252)"> --><FONT COLOR="#000077"><B>Figure 58.5</B></FONT></A> <I>Efficient method for advancing source texture pointer.</I>
|
|
</P>
|
|
<P><A NAME="Fig6"><!-- </A><A HREF="javascript:displayWindow('images/58-06.jpg',408,115 )"> --><IMG SRC="images/58-06.jpg"><BR><!-- </A>
|
|
<BR><A HREF="javascript:displayWindow('images/58-06.jpg',408,115)"> --><FONT COLOR="#000077"><B>Figure 58.6</B></FONT></A> <I>Storing both X and Y fractional coordinates in one register.</I>
|
|
</P>
|
|
<P>Actually, we also advanced the source pointer by the Y integer amount back when we added BP to SI; all that’s left is to detect whether our addition to the Y fractional current coordinate produced a carry. That’s easily done by testing bit 15 of EDX; if it’s zero, there was no carry and we’re done; otherwise, Y carried, so we have to reset bit 15 and advance the source pointer by one scanline. The resulting program flow is shown in Figure 58.7. Note that unlike the X fractional addition, we can’t get away with just adding in the carry from the Y fractional addition, because when the Y fraction carries, it indicates a move not from one pixel to the next on a scanline (a single byte), but rather from one scanline to the next (a full scanline width).
|
|
</P><P><BR></P>
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="58-02.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="58-04.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
|
|
<hr width="90%" size="1" noshade>
|
|
<div align="center">
|
|
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book © 2001 Michael Abrash</font>
|
|
</div>
|
|
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
|
|
<!-- reference_subfoot = footer -->
|
|
<!-- reference_footer = subfoot -->
|
|
|
|
<!-- BEGIN SUB FOOTER -->
|
|
</BODY>
|
|
</HTML>
|
|
|
|
<!-- END FOOTER -->
|
|
|
|
|