INFO: Improving Windows CE Video Driver Performance: Line Drawing Operations (262127)



The information in this article applies to:

  • Microsoft Windows CE Platform Builder 2.11
  • Microsoft Windows CE Platform Builder 2.12
  • Microsoft Windows CE Platform Builder 3.0

This article was previously published under Q262127

SUMMARY

Most Windows CE display drivers use the Graphics Primitive Engine (GPE) to provide default processing for display driver primitives. The GPE::Line method draws lines on the destination surface using the parameters provided in the GPELineParms and EGPEPhase data structures. This article defines the GPELineParms and EGPEPhase data structures and describes how GPE draws a line.

The default drawing provided by GPE may be replaced in the display driver by software-emulated accelerations or by native hardware accelerations, as discussed in the Windows CE 2.12 Device Driver Kit chapter about display drivers.

MORE INFORMATION

GPELineParms Structure

GPELineParms is a structure. GPELineParms is defined in this directory of the Platform Builder:

Public\Common\Oak\Inc

GPELineParms is used by GPE and the display driver to control line drawing. Some of the parameters in the structure come directly from the application or the current device context. The rest are calculated by GPE.
struct GPELineParms
{
	SCODE 			(GPE::*pLine)(GPELineParms *);
	long				xStart;  //Starting point of line, in X direction
	long				yStart;  //Starting point of line, in Y direction
	int				cPels;  //Length of line in major direction, in pixels
	unsigned long	dM;  //larger of |xStart-Xend| and |yStart-yEnd|, calculated in 1/16ths of a pixel
	unsigned long	dN;  //smaller of |xStart-Xend| and |yStart-yEnd|, calculated in 1/16ths of a pixel
	long				llGamma;  //typically used as the initial starting value for the slope iterator
	int				iDir;  //Octant number for line
	unsigned long	style;  //IN parameter - line attributes (solid or dashed)
	int				styleState;  //state of line style in current path, in pixels
	GPESurf 			*pDst;  //IN parameter - destination surface
	COLOR			solidColor;  //IN parameter brush color
	RECTL			*prclClip;  //clipping rectangle
	unsigned short	mix;  //IN parameter ROP2 values for mark and space
};
The GPELineParms style is the line style. A value of zero (0) implies that it is a solid line (that is, a single ROP2) whereas a style of 0xAAAAAAAA is single-pixel dots (alternating between two ROP2s). As passed into the DrvStrokePath function, the flag LA_ALTERNATE in plineattrs->fl implies a style of 0xAAAAAAAA. Alternatively, you can use the pstyle and cstyle parameters to make complex dashed or dotted styles. For example, 0xF0F0F0F0 in pParms->style implies longer dashes.

GPELineParms mix is the ROP2 value. It consists of two bytes, a ROP2 for mark and a ROP2 for space. The value of each bit in the style chooses between the two bytes in the pParms->mix value. GPELineParms iDir is the octant number for the line (one of 8 values, per the Bresenham algorithm). Octant Axis X-Dir Y-Dir 0 X-Major +1 +1 1 Y-Major +1 +1 2 Y-Major -1 +1 3 X-Major -1 +1 4 X-Major -1 -1 5 Y-Major -1 -1 6 Y-Major +1 -1 7 X-Major +1 -1

EGPEPHASE Enumerated Type

EGPEPHASE is an enumerated type. EGPEPHASE is defined in the following directory of the Platform Builder:

Public\Common\Oak\Inc

enum EGPEPhase
{
	gpeSingle,
	gpePrepare,  //device-specific initialization
	gpeContinue,
	gpeComplete  //device-specific clean up
};

Line Drawing Processing

Prepare Phase

Line drawing begins when an Application calls the Polyline method. GDI handles the call to the Polyline method. GDI validates that there are at least two vertices and that the line style is not null. GDI then calls the GPE DrvStrokePath function, or for a wide pen, the DrvFillPath function. When GDI calls the GPE member function, it passes along information from the device context:
  • Surface object
  • Path object
  • Clip rectangle
  • The brush
  • The ROP2 value
  • The line attributes (solid or dashed).
These parameters were either set by the application or are realized attributes. GPE uses some of the input parameters to populate its GPELineParms structure.

GPE then calls the display driver's Line function, passing in the GPELineParms and passing in an EGPEPHASE setting of gpePrepare. The driver examines the properties of the line that is to be drawn. The driver then chooses whether it wants to use the GPE EmulatedLine function or handle the line with its own accelerated function. Before selecting an accelerated function, the driver must check whether the hardware can handle the line style and ROP. In the S3Trio64 display driver (in the Platform Builder directory Platform\Cepc\Drivers\Display\S3trio64), the driver sets the function pointer by default to EmulatedLine, but chooses hardware acceleration for lines that are both in video memory and use ROP 0x0D0D. The following sample code illustrates this:

#ifdef ENABLE_ACCELERATION
	if( phase == gpeSingle || phase == gpePrepare )
	{
		pLineParms->pLine = EmulatedLine;
//		pLineParms->pLine = (SCODE (GPE::*)(struct GPELineParms *))EmulatedLine;
		if( pLineParms->pDst->InVideoMemory() && pLineParms->mix == 0x0d0d )
		{
			SelectSolidColor( pLineParms->solidColor );
			pLineParms->pLine = (SCODE (GPE::*)(struct GPELineParms *))AcceleratedSolidLine;
		}
//		if( pLineParms->mix == 0x0B07 )	   .. dotted line

	}
#else
		pLineParms->pLine = EmulatedLine;
#endif
Also, in the prepare phase, the driver can optionally perform additional processing such as initializing the hardware registers with the selected color. After the prepare phase, control returns to the GPE DrvStrokePath function.

DrvStrokePath Manages Drawing of Each Segment

After determining the driver's line drawing function, the DrvStrokePath function carries out the following loops. The ordering of these loops is intended to maximize hardware clipping. Typically, a path falls into just one of the clipping rectangles.
	for( each clip rectangle in clip list )
	{
		for( each line segment in stroke path )
		{
			calculate line segment clipped to current cliprect

			pParms->pLine(pParms);
		}
	}
The DrvStrokePath function clips the line segments to the clipping rectangle. It is not necessary for the driver to do that in its Line function. In fact, the driver is really prevented from clipping because there is no valid clipping information during the prepare phase. There may be no clipping rectangle or there may be an entire sequence of clipping rectangles for a single prepare call.
  • EmulatedLine is the Default Line Drawing Function

    The EmulatedLine function is the GPE line drawing function. EmulatedLine implements the following algorithm:
    	long accum = (long)(pParms->dN) + pParms->llGamma;
    	long axstp = (long)(pParms->dN);
    	long dgstp = (long)(pParms->dN) - (long)(pParms->dM);
    
    for( remainingPels = pParms->cPels; remainingPels; remainingPels-- )
    	{
    		if( axstp )		//  ( axstp == 0 implies horizontal or vertical line being drawn )
    		{
    			if( accum < 0 )
    			{
    				accum += axstp;
    			}
    			else
    			{
    				increment_in_minor_direction;
    				accum += dgstp;
    			}
    		}
    		increment_in_major_direction;
    	}
    Recommended Usage of EmulatedLine

    Use the EmulatedLine function to handle diagonal lines. Diagonal lines are relatively rare and typically do not need to be accelerated. Similarly, it is quite rare for line styles to be used. The EmulatedLine function should be used as the default processor for this case as well.

  • Driver Can Replace Default Function

    For devices that support hardware line drawing acceleration, the driver can implement an accelerated line function to replace the default EmulatedLine function. Alternatively, the driver can call an existing function or create a new software emulation function to handle the line drawing. Even if inadequate line drawing acceleration is available, if solid fill block transfer (BLT) acceleration is implemented in the hardware then this should be used to accelerate horizontal and vertical lines.

    Hardware Acceleration of Horizontal and Vertical Lines Only

    The recommended approach for most situations is to emulate all diagonal and styled lines, using either line drawing hardware or fill-blt hardware to accelerate horizontal and vertical lines that comprise the majority of lines drawn by Windows CE. For horizontal and vertical lines, the dN parameter is zero (0) and no account need be taken of the sub-pixel information in the dM parameter and error term llgamma.
    if( pLineParms->dN == 0 )
    {
    	// Line is vertical or horizontal
    	// Use fill-blt or h/w line to draw a line starting at
    	// pLineParms->xStart, pLineParms->yStart
    	// in the direction specified by pLineParms->iDir
    	// for a total of pLineParms->cPels pixels
    }
    else
    {
    	return EmulatedLine( pLineParms );	// use S/W for diagonal line
    }
Hardware Acceleration of Diagonal Lines

It is important to note that for diagonal lines, the values in the dM parameter, dN parameter, and error term llgamma fields of GPELineParms contain sub-pixel information. This information should be used when initializing hardware line drawing registers. Otherwise, diagonal lines that are clipped are incorrectly drawn. This effect can be quite noticeable when moving a window over another window that contains a diagonal line.

In the prepare phase, the driver performs a general inspection of the line parameters to determine if it can accelerate it. When the acceleration function is called, the driver may need to perform additional validation. This is because when the driver's accelerated function is called, it is for a specific path segment, clipped to a specific clipping region. The driver needs to ensure that, for example, the line segment length will not cause its hardware registers to overflow. For example, in the S3Trio64 driver, this additional level of screening takes place in the code that follows. Because the dM and dN values are originally expressed in 1/16ths of pixels, the device's hardware slope iterators need to be able to retain these lengths for whatever line segment is being drawn. A lot of hardware is designed to be able to draw a diagonal line across the whole screen but expects the dM, and the dN values (or equivalents) to be expressed in pixels. Because GPE uses sub-pixel precision, these counters need four (4) more bits. For short lines, it is not a problem but for long lines these values can overflow the registers resulting in lines just zinging off to some edge of the screen or even wrapping around a few times.

While diagonal lines are uncommon, long diagonal lines are extremely rare so the time taken to render long diagonal lines using the EmulatedLine function is unimportant.
int errTerm = (int)pLineParms->dN + (int)pLineParms->llGamma - ( ( command & PLUS_X ) ? 0 : 1 );

	if(
		( pLineParms->dN > 4090 ) ||
		( pLineParms->dM-pLineParms->dN > 4090 ) ||		// remember dM >= dN
		( errTerm > 4090 ) ||
		( errTerm < -4090 ) )
	{
		RetVal = EmulatedLine( pLineParms );	// The hardware DDA would overflow so use the emulation
	}
	else
	{
		WaitForFIFO(7); // seven parameters required
		reg_ALT_CURXY = ((pLineParms->xStart + ((S3Trio64Surf *)(pLineParms->pDst))->Left()) << 16 ) |
						(pLineParms->yStart + ((S3Trio64Surf *)(pLineParms->pDst))->Top());
		reg_MAJ_AXIS_PCNT = (pLineParms->cPels-1);
		reg_ALT_STEP = (((pLineParms->dN-pLineParms->dM)/*&0x3FFF*/)<<16) | (pLineParms->dN /*&0x3FFF*/);
		reg_ERR_TERM = errTerm /*&0x3FFF*/;
		reg_CMD = command;
		RetVal = S_OK;
	}

Complete Phase

After the DrvStrokePath function completes line drawing, DrvStrokePath again calls the driver's Line function this time passing it the EGPEphase set to gpeComplete. The driver should clean up any line drawing state required by the hardware at this point.

Modification Type:MinorLast Reviewed:8/18/2005
Keywords:kbinfo KB262127