87 lines
8.2 KiB
HTML
87 lines
8.2 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: On Story, Two Rules, and a BSP Renderer</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=62//-->
|
|
<!--PAGES=1145-1149//-->
|
|
<!--UNASSIGNED1//-->
|
|
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
|
|
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="61-04.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="62-02.html">Next</A></TD>
|
|
</TR>
|
|
</TABLE>
|
|
</CENTER>
|
|
<P><BR></P>
|
|
<H2><A NAME="Heading1"></A><FONT COLOR="#000077">Chapter 62<BR>One Story, Two Rules, and a BSP Renderer
|
|
</FONT></H2>
|
|
<H3><A NAME="Heading2"></A><FONT COLOR="#000077">Taking a Compiled BSP Tree from Logical to Visual Reality</FONT></H3>
|
|
<P>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 startling discovery that the program ran nearly twice as fast with page flipping as it did with the alternative method of drawing the whole frame to system memory, then copying it to the screen. We were delighted by this, but baffled. I did a few tests and came up with several possible explanations, including slow writes through the external cache, poor main memory performance, and cache misses when copying the frame from system memory to video memory. Although each of these can indeed affect performance, none seemed to account for the magnitude of the speedup, so I assumed there was some hidden hardware interaction at work. Anyway, “why” was secondary; what really mattered was that we had a way to double performance, which meant I had a lot of work to do to support page flipping as widely as possible.
|
|
</P>
|
|
<P>A few days ago, I was using the Pentium’s built-in performance counters to seek out areas for improvement in Quake and, for no particular reason, checked the number of writes performed while copying the frame to the screen in non-page-flipped mode. The answer was 64,000. That seemed odd, since there were 64,000 byte-sized pixels to copy, and I was calling <B>memcpy()</B>, which of course performs copies a dword at a time whenever possible. I thought maybe the Pentium counters report the number of bytes written rather than the number of writes performed, but fortunately, this time I tested my assumptions by writing an ASM routine to copy the frame a dword at a time, without the help of <B>memcpy()</B>. This time the Pentium counters reported 16,000 writes.</P>
|
|
<P>Whoops.</P>
|
|
<P>As it turns out, the <B>memcpy()</B> routine in the DOS version of our compiler (gcc) inexplicably copies memory a byte at a time. With my new routine, the non-page-flipped approach suddenly became slightly <I>faster</I> than page flipping.</P>
|
|
<P>The first relevant rule is pretty obvious: <I>Assume nothing</I>. Measure early and often. Know what’s really going on when your program runs, if you catch my drift. To do otherwise is to risk looking mighty foolish.</P>
|
|
<P>The second rule: When you do look foolish (and trust me, it <I>will</I> happen if you do challenging work) have a good laugh at yourself, and use it as a reminder of Rule #1. I hadn’t done any extra page-flipping work yet, so I didn’t waste any time due to my faulty assumption that <B>memcpy()</B> performed a maximum-speed copy, but that was just luck. I should have done experiments until I was sure I knew what was going on before drawing any conclusions and acting on them.</P>
|
|
<TABLE WIDTH="100%"><TD WIDTH="5%" VALIGN="TOP" ALIGN="LEFT"><IMG SRC="images/62-01i.jpg"><TD WIDTH="95%" VALIGN="TOP" ALIGN="LEFT"><SMALL><I>In general, make it a point not to fall into a tightly focused rut; stay loose and think of alternative possibilities and new approaches, and always, always, always keep asking questions. It’ll pay off big in the long run. If I hadn’t indulged my curiosity by running the Pentium counter test on the copy to the screen, even though there was no specific reason to do so, I would never have discovered the <B>memcpy()</B> problem—and by so doing I doubled the performance of the entire program in five minutes, a rare accomplishment indeed.</I></SMALL>
|
|
</TABLE>
|
|
<P>By the way, I have found the Pentium’s performance counters to be very useful in of information on the performance counters and other aspects of the Pentium is Mike Schmit’s book, <I>Pentium Processor Optimization Tools</I>, AP Professional, ISBN 0-12-627230-1.</P>
|
|
<P>Onward to rendering from a BSP tree.</P>
|
|
<H4 ALIGN="LEFT"><A NAME="Heading3"></A><FONT COLOR="#000077">BSP-based Rendering</FONT></H4>
|
|
<P>For the last several chapters I’ve been discussing the nature of BSP (Binary Space Partitioning) trees, and in Chapter 60 I presented a compiler for 2-D BSP trees. Now we’re ready to use those compiled BSP trees to do realtime rendering.
|
|
</P>
|
|
<P>As you’ll recall, the BSP compiler took a list of vertical walls and built a 2-D BSP tree from the walls, as viewed from above. The result is shown in Figure 62.1. The world is split into two pieces by the line of the root wall, and each half of the world is then split again by the root’s children, and so on, until the world is carved into subspaces along the lines of all the walls.</P>
|
|
<P><A NAME="Fig1"><!-- </A><A HREF="javascript:displayWindow('images/62-01.jpg',400,153 )"> --><IMG SRC="images/62-01.jpg"><BR><!-- </A>
|
|
<BR><A HREF="javascript:displayWindow('images/62-01.jpg',400,153)"> --><FONT COLOR="#000077"><B>Figure 62.1</B></FONT></A> <I>Vertical walls and a BSP tree to represent them.</I>
|
|
</P>
|
|
<P>Our objective is to draw the world so that whenever walls overlap we see the nearer wall at each overlapped pixel. The simplest way to do that is with the painter’s algorithm; that is, drawing the walls in back-to-front order, assuming no polygons interpenetrate or form cycles. BSP trees guarantee that no polygons interpenetrate (such polygons are automatically split), and make it easy to walk the polygons in back-to-front (or front-to-back) order.
|
|
</P>
|
|
<P>Given a BSP tree, in order to render a view of that tree, all we have to do is descend the tree, deciding at each node whether we’re seeing the front or back of the wall at that node from the current viewpoint. We use that knowledge to first recursively descend and draw the farther subtree of that node, then draw that node, and finally draw the nearer subtree of that node. Applied recursively from the root of our BSP trees, this approach guarantees that overlapping polygons will always be drawn in back-to-front order. Listing 62.1 draws a BSP-based world in this fashion. (Because of the constraints of the printed page, Listing 62.1 is only the core of the BSP renderer, without the program framework, some math routines, and the polygon rasterizer; but, the entire program is on the CD-ROM as DDJBSP2.ZIP. Listing 62.1 is in a compressed format, with relatively little whitespace; the full version on the CD-ROM is formatted normally.)</P><P><BR></P>
|
|
<CENTER>
|
|
<TABLE BORDER>
|
|
<TR>
|
|
<TD><A HREF="61-04.html">Previous</A></TD>
|
|
<TD><A HREF="index.html">Table of Contents</A></TD>
|
|
<TD><A HREF="62-02.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 -->
|
|
|
|
|