225 lines
6.3 KiB
Markdown
225 lines
6.3 KiB
Markdown
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.*
|
|
------------------- ---------------------------------------------------------------------------------------------------------
|