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
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
|