237 lines
6.9 KiB
Markdown
237 lines
6.9 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](39-01.html) [Table of Contents](index.html) [Next](39-03.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
At any rate, Listing 39.1 for this chapter shows a version of
|
||
**DrawHorizontalLineList** that uses memset to draw each scan line of
|
||
the polygon in a single call. When linked to Chapter 38’s test program,
|
||
Listing 39.1 increases pure drawing speed (disregarding edge tracing and
|
||
other nondrawing time) by more than an order of magnitude over Chapter
|
||
38’s draw-pixel-based code, despite the fact that Listing 39.1 requires
|
||
a large (in this case, the Compact) data model. Listing 39.1 works fine
|
||
with Borland C++, but may not work with other compilers, for it relies
|
||
on the aforementioned interaction between memset and the selected memory
|
||
model.
|
||
|
||
* * * * *
|
||
|
||
Implementation
|
||
|
||
Total Polygon\
|
||
Filling Time
|
||
|
||
DrawHorizontal\
|
||
LineList
|
||
|
||
ScanEdge
|
||
|
||
FillConvex\
|
||
Polygon
|
||
|
||
* * * * *
|
||
|
||
Drawing to display memory in mode 13h
|
||
|
||
C floating point scan/DrawPixel drawing code from Chapter 38, (small
|
||
model)
|
||
|
||
11.69
|
||
|
||
5.80 seconds\
|
||
(50% of total)
|
||
|
||
5.86\
|
||
(50%)
|
||
|
||
0.03\
|
||
(\<1%)
|
||
|
||
C floating point scan/memset drawing (Listing 39.1, compact model)
|
||
|
||
6.64
|
||
|
||
0.49\
|
||
(7%)
|
||
|
||
6.11\
|
||
(92%)
|
||
|
||
0.04\
|
||
(\<1%)
|
||
|
||
C integer scan/memset drawing (Listing 39.1 & Listing 39.2, compact
|
||
model)
|
||
|
||
0.60
|
||
|
||
0.49\
|
||
(82%)
|
||
|
||
0.07\
|
||
(12%)
|
||
|
||
0.04\
|
||
(7%)
|
||
|
||
C integer scan/ASM drawing (Listing 39.2 & Listing 39.3, small model)
|
||
|
||
0.45
|
||
|
||
0.36\
|
||
(80%)
|
||
|
||
0.06\
|
||
(13%)
|
||
|
||
0.03\
|
||
(7%)
|
||
|
||
ASM integer scan/ASM drawing (Listing 40.3 & Listing 40.4,small model)
|
||
|
||
0.42
|
||
|
||
0.36\
|
||
(86%)
|
||
|
||
0.03\
|
||
(7%)
|
||
|
||
0.03\
|
||
(7%)
|
||
|
||
Drawing to system memory
|
||
|
||
C integer scan/memset drawing (Listing 39.1 & Listing 39.2,compact
|
||
model)
|
||
|
||
0.31
|
||
|
||
0.20\
|
||
(65%)
|
||
|
||
0.07\
|
||
(23%)
|
||
|
||
0.04\
|
||
(13%)
|
||
|
||
ASM integer scan/ASM drawing (Listing 39.3 & Listing 39.4,small model)
|
||
|
||
0.13
|
||
|
||
0.07\
|
||
(54%)
|
||
|
||
0.03\
|
||
(23%)
|
||
|
||
0.03\
|
||
(23%)
|
||
|
||
All times are in seconds, as measured with Turbo Profiler on a 20-MHz
|
||
cached 386 with no math coprocessor installed. Note that time spent in
|
||
**main()** is not included. C code was compiled with Borland C++ with
|
||
maximum optimization (-G -O -Z -r -a); assembly language code was
|
||
assembled with TASM. Percentages of combined times are rounded to the
|
||
nearest percent, so the sum of the three percentages does not always
|
||
equal 100.
|
||
|
||
* * * * *
|
||
|
||
Table 39.1 Polygon fill performance.
|
||
|
||
* * * * *
|
||
|
||
**LISTING 39.1 L39-1.C**
|
||
|
||
/* Draws all pixels in the list of horizontal lines passed in, in
|
||
mode 13h, the VGA’s 320x200 256-color mode. Uses memset to fill
|
||
each line, which is much faster than using DrawPixel but requires
|
||
that a large data model (compact, large, or huge) be in use when
|
||
running in real mode or 286 protected mode.
|
||
All C code tested with Borland C++. */
|
||
|
||
#include <string.h>
|
||
#include <dos.h>
|
||
#include “polygon.h”
|
||
|
||
#define SCREEN_WIDTH 320
|
||
#define SCREEN_SEGMENT 0xA000
|
||
|
||
void DrawHorizontalLineList(struct HLineList * HLineListPtr,
|
||
int Color)
|
||
{
|
||
struct HLine *HLinePtr;
|
||
int Length, Width;
|
||
unsigned char far *ScreenPtr;
|
||
|
||
/* Point to the start of the first scan line on which to draw */
|
||
ScreenPtr = MK_FP(SCREEN_SEGMENT,
|
||
HLineListPtr->YStart * SCREEN_WIDTH);
|
||
|
||
/* Point to the XStart/XEnd descriptor for the first (top)
|
||
horizontal line */
|
||
HLinePtr = HLineListPtr->HLinePtr;
|
||
/* Draw each horizontal line in turn, starting with the top one and
|
||
advancing one line each time */
|
||
Length = HLineListPtr->Length;
|
||
while (Length-- > 0) {
|
||
/* Draw the whole horizontal line if it has a positive width */
|
||
if ((Width = HLinePtr->XEnd - HLinePtr->XStart + 1) > 0)
|
||
memset(ScreenPtr + HLinePtr->XStart, Color, Width);
|
||
HLinePtr++; /* point to next scan line X info */
|
||
ScreenPtr += SCREEN_WIDTH; /* point to next scan line start */
|
||
}
|
||
}
|
||
|
||
At this point, I’d like to mention that benchmarks are notoriously
|
||
unreliable; the results in Table 39.1 are accurate *only* for the test
|
||
program, and only when running on a particular system. Results could be
|
||
vastly different if smaller, larger, or more complex polygons were
|
||
drawn, or if a faster or slower computer/VGA combination were used.
|
||
These factors notwithstanding, the test program does fill a variety of
|
||
polygons of varying complexity sized from large to small and in between,
|
||
and certainly the order of magnitude difference between Listing 39.1 and
|
||
the old version of **DrawHorizontalLineList** is a clear indication of
|
||
which code is superior.
|
||
|
||
Anyway, Listing 39.1 has the desired effect of vastly improving drawing
|
||
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}
|
||
|
||
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
|
||
integer or fixed-point calculations, instead. (Sure, the floating point
|
||
code would run faster if a math coprocessor were installed, but it would
|
||
still be slower than the alternatives; besides, why require a math
|
||
coprocessor when you don’t have to?) Both integer and fixed-point
|
||
calculations are fast. In many cases, fixed-point is faster, but integer
|
||
calculations have one tremendous virtue: They’re completely accurate.
|
||
The tiny imprecision inherent in either fixed or floating-point
|
||
calculations can result in occasional pixels being one position off from
|
||
their proper location. This is no great tragedy, but after going to so
|
||
much trouble to ensure that polygons don’t overlap at common edges, why
|
||
not get it exactly right?
|
||
|
||
In fact, when I tested out the integer edge tracing code by comparing an
|
||
integer-based test image to one produced by floating-point calculations,
|
||
two pixels out of the whole screen differed, leading me to suspect a bug
|
||
in the integer code. It turned out, however, that’s in those two cases,
|
||
the floating point results were sufficiently imprecise to creep from
|
||
just under an integer value to just over it, so that the **ceil**
|
||
function returned a coordinate that was one too large.
|
||
|
||
------------------- ---------------------------------------------------------------------------------------------------------
|
||
 *Floating point is very accurate—but it is not precise. Integer calculations, properly performed, are.*
|
||
------------------- ---------------------------------------------------------------------------------------------------------
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](39-01.html) [Table of Contents](index.html) [Next](39-03.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|