Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

257 lines
10 KiB

///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 1998.
//
// fragproc.cpp
//
// Direct3D Reference Rasterizer - Fragment Processing Methods
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop
//
// Fragments are managed by a separate 'surface' of fragment pointers which
// form an (initially empty) linked list of fragment structures for each pixel.
//
// The fragment management consists of generating fragments for partially
// covered pixels (either due to coverage mask or non-opaque alpha), and
// freeing fragments which are obscurred by a fully covered pixel in
// front of them.
//
// The fragment generation occurs when a new pixel is partially covered.
// If the pixel location already has at least one fragment, then a fragment
// merge is attempted. This merge is attempted with the fragment most recently
// added to the pixel (at the front of the linked list), which has the best
// chance of merging since it is most likely to be from the same object.
// The merge tests the Z and color values, and if they are within a threshold
// then the new fragmented pixel's contribution is OR'd into the existing
// fragment instead of generating a new fragment. The depth merge criteria is
// an absolute value compare. The color merge criteria is done with a bitmask
// (because ripping apart the color into channels for the value compare is too
// expensive). Set bits in bitmask FRAGMERGE_COLORDIFF_MASK are bits for which
// the two colors must match. This actually works pretty well...
//
// If the merge fails, then a new fragment is allocated, filled out, and added
// to the linked list for this pixel location.
//
// If the merge results in a fully covered pixel, then the fragment is freed
// and the fragment's color and depth are written to the color/depth buffers.
//
//
// controls for fragment merging
//
// TODO - not sure that merge works correctly right now...
//#define DO_FRAGMERGE
// mask for crude (but fast) color differencing - this is not so fast now
// that colors are stored as floats...
#define FRAGMERGE_COLORDIFF_MASK 0xe0c0e0c0
// depth difference must be less than this for merge to occur
FLOAT g_fFragMergeDepthThreshold = 1.F/(FLOAT)(1<<16);
//-----------------------------------------------------------------------------
//
// DoFragmentGenerationProcessing - Does initial work of generating a fragment
// buffer entry (if appropriate) and filling it out. Also attempts fragment
// merge.
//
// Returns TRUE if processing for this pixel is complete.
//
//-----------------------------------------------------------------------------
BOOL
ReferenceRasterizer::DoFragmentGenerationProcessing( RRPixel& Pixel )
{
// TRUE if pixel is geometrically partially covered
BOOL bDoFragCvg = ( TL_CVGFULL != Pixel.CvgMask );
// TRUE if pixel is partially covered due to transparency
BOOL bDoFragTransp = FALSE;
if ( m_dwRenderState[D3DRENDERSTATE_TRANSLUCENTSORTINDEPENDENT] )
{
// only generate fragments for transparency if
// D3DRENDERSTATE_TRANSLUCENTSORTINDEPENDENT is enabled and the
// alpha is less than the threshold
bDoFragTransp = ( UINT8(Pixel.Color.A) < g_uTransparencyAlphaThreshold );
}
else
{
// so we won't use alpha for determining transparency in fragment resolve
Pixel.Color.A = 1.0F;
}
// get pointer to fragment list for this pixel location - may be NULL due to
// deferred allocation of fragment buffer
RRFRAGMENT** ppFrag = (NULL == m_ppFragBuf) ? (NULL)
: (m_ppFragBuf + (m_pRenderTarget->m_iWidth*Pixel.iY) + Pixel.iX);
if ( bDoFragCvg || bDoFragTransp )
{
// get pointer to pointer to first fragment in linked list for this pixel
if (NULL == m_ppFragBuf)
{
// do (deferred) allocation of fragment pointer buffer - clear this
// initially and it will always be cleared during the fragment resolve process
size_t cbBuf = sizeof(RRFRAGMENT*)*m_pRenderTarget->m_iWidth*m_pRenderTarget->m_iHeight;
// allocate fragment pointer buffer for rendering core - clear initially
m_ppFragBuf = (RRFRAGMENT**)MEMALLOC( cbBuf );
_ASSERTa( NULL != m_ppFragBuf, "malloc failure on RRFRAGMENT pointer buffer",
return FALSE; );
memset( m_ppFragBuf, 0x0, cbBuf );
// ppFrag only not set if (NULL == m_ppFragBuf)
ppFrag = (m_ppFragBuf + (m_pRenderTarget->m_iWidth*Pixel.iY) + Pixel.iX);
}
#ifdef DO_FRAGMERGE
// try to do fragment merge
if ( NULL != (*ppFrag) )
{
// check if new depth is close enough to depth of first frag in list
FLOAT fDepthDiff = fabs( FLOAT((*ppFrag)->Depth) - FLOAT(Pixel.Depth) );
BOOL bDepthClose = ( fDepthDiff < g_fFragMergeDepthThreshold );
// check if new color is close enough to color of first frag in list
UINT32 uARGBSame = ~( UINT32(Pixel.Color) ^ UINT32((*ppFrag)->Color) );
BOOL bColorClose = ( FRAGMERGE_COLORDIFF_MASK == ( uARGBSame & FRAGMERGE_COLORDIFF_MASK ) );
if ( bDepthClose && bColorClose )
{
m_pStt->cFragsMerged++;
// here to do merge
CVGMASK FirstFragCvgMask = (*ppFrag)->CvgMask;
CVGMASK MergedCvgMask = FirstFragCvgMask | Pixel.CvgMask;
// check for merge to full coverage
if ( ( TL_CVGFULL == MergedCvgMask ) && !bDoFragTransp )
{
m_pStt->cFragsMergedToFull++;
// free first fragment
RRFRAGMENT* pFragFree = (*ppFrag); // keep ptr to frag to free
(*ppFrag) = (RRFRAGMENT*)(*ppFrag)->pNext; // set buffer to point to next
FragFree( pFragFree);
// now need to write this pixel into pixel buffer, so return
// FALSE so pixel processing will continue
return FALSE;
}
else
{
// mask not full, so update first frag's cm and done
(*ppFrag)->CvgMask = MergedCvgMask;
// done with this pixel
return TRUE;
}
}
// else fall into allocating new frag
}
#endif
// allocate and fill fragment
RRFRAGMENT* pFragNew = FragAlloc();
if ( NULL == pFragNew ) { return FALSE; }
pFragNew->Color = Pixel.Color;
pFragNew->Depth = Pixel.Depth;
pFragNew->CvgMask = Pixel.CvgMask;
// insert at front of list (before fragment we're looking at)
pFragNew->pNext = (void*)(*ppFrag);
(*ppFrag) = pFragNew;
// done with this pixel
return TRUE;
}
// not done with this pixel
return FALSE;
}
//-----------------------------------------------------------------------------
//
// DoFragmentBufferFixup - Routine to free fragments which are behind
// fully covered sample just written into the pixel buffer. This minimizes
// the total number of fragment needed for a scene. This step involves walking
// the linked list and freeing fragments behind the pixel about to be written.
// This also simplifies the fragment resolve since the Z buffer is not needed
// (all fragments are known to be in front of the fully-covered sample in the
// color/Z buffer).
//
//-----------------------------------------------------------------------------
void
ReferenceRasterizer::DoFragmentBufferFixup( const RRPixel& Pixel )
{
// get pointer to fragment list for this pixel location - may be NULL due to
// deferred allocation of fragment buffer
RRFRAGMENT** ppFrag = (NULL == m_ppFragBuf)
? (NULL)
: (m_ppFragBuf + (m_pRenderTarget->m_iWidth*Pixel.iY) + Pixel.iX);
//
// walk fragment array to free fragments behind covered sample
//
if ( NULL != ppFrag )
{
while ( NULL != (*ppFrag) )
{
if ( DepthCloser( Pixel.Depth, (*ppFrag)->Depth ) )
{
// covered sample is closer than fragment, so free the frag
RRFRAGMENT* pFragFree = (*ppFrag); // keep ptr to frag to free
(*ppFrag) = (RRFRAGMENT*)(*ppFrag)->pNext; // remove from list
// ppFrag now points to a pointer to the next frag
FragFree( pFragFree );
}
else
{
// advance pointer to point to pointer to next frag
ppFrag = (RRFRAGMENT **)&((*ppFrag)->pNext);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// Fragment Allocation Methods //
// //
///////////////////////////////////////////////////////////////////////////////
//
// These methods are used by the pixel engine and fragment resolver to
// allocate and free fragments.
//
//-----------------------------------------------------------------------------
//
// Allocates a single fragment record, returning pointer
//
//-----------------------------------------------------------------------------
RRFRAGMENT*
ReferenceRasterizer::FragAlloc( void )
{
RRFRAGMENT* pFrag = (RRFRAGMENT*)MEMALLOC( sizeof(RRFRAGMENT) );
_ASSERTa( NULL != pFrag, "malloc failed on RRFRAGMENT", return NULL; );
// update stats
m_pStt->cFragsAllocd++;
if (m_pStt->cFragsAllocd > m_pStt->cMaxFragsAllocd ) { m_pStt->cMaxFragsAllocd = m_pStt->cFragsAllocd; }
return pFrag;
}
//-----------------------------------------------------------------------------
//
// Frees a single fragment record
//
//-----------------------------------------------------------------------------
void
ReferenceRasterizer::FragFree( RRFRAGMENT* pFrag )
{
MEMFREE( pFrag );
// update stats
m_pStt->cFragsAllocd--;
}
///////////////////////////////////////////////////////////////////////////////
// end