130 lines
7.5 KiB
Markdown
130 lines
7.5 KiB
Markdown
---
|
|
title: Michael Abrash's Graphics Programming Black Book, Special Edition
|
|
author: Michael Abrash
|
|
date: '1997-07-01'
|
|
isbn: '1576101746'
|
|
publisher: The Coriolis Group
|
|
category: 'Web and Software Development: Game Development,Web and Software Development:
|
|
Graphics and Multimedia Development'
|
|
chapter: '39'
|
|
pages: 727-730
|
|
---
|
|
|
|
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%) |
|
|
|
|
Table: Table 39.1 Polygon fill performance.
|
|
|
|
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.
|
|
|
|
**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.*
|
|
------------------- ---------------------------------------------------------------------------------------------------------
|