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.
 
 
 
 
 
 

498 lines
19 KiB

///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 1998.
//
// fragrslv.cpp
//
// Direct3D Reference Rasterizer - Fragment Resolve Methods
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop
// fragment resolution controls - need to expose these somehow (maybe...)
static BOOL g_bPreMultAlpha = TRUE;
static BOOL g_bDoCoverageOnly = FALSE;
//-----------------------------------------------------------------------------
//
// CountFrags - Utility to count fragments in a linked list.
//
//-----------------------------------------------------------------------------
int
CountFrags(RRFRAGMENT* pFrag)
{
if (g_iDPFLevel < 4) { return 0; }
int iRet = 0;
while ( NULL != pFrag ) { pFrag = (RRFRAGMENT* )pFrag->pNext; iRet++; }
return iRet;
}
//-----------------------------------------------------------------------------
//
// DPFFrags - Utility to debug-print fragment list.
//
//-----------------------------------------------------------------------------
void
DPFFrags(RRFRAGMENT* pFrag)
{
if (g_iDPFLevel < 7) { return; }
while (NULL != pFrag)
{
DPFM(7,FRAG,(" (%06x,%06x) %08x %f %04x\n",
pFrag,pFrag->pNext,UINT32(pFrag->Color),FLOAT(pFrag->Depth),pFrag->CvgMask))
pFrag = (RRFRAGMENT *)pFrag->pNext;
}
}
//-----------------------------------------------------------------------------
//
// DoFragResolve - Invoked during the buffer-resolve for each pixel which
// has fragments. Takes a pointer to a linked list of fragments and returns
// the resolved color.
//
// This constists of two steps: fragment sorting; and fragment resolve
// accumulation. The fragments are sorted by stepping through the original
// linked list and moving the fragments into a new linked list (using the
// same link pointers) which is sorted in Z.
//
// The fragment resolve occurs in one of two ways depending on if any non-opaque
// fragments exist in the list (this is determined during the sort). If there
// are only opaque fragments, then the resolve accumulation depends only on
// the coverage masks and is thus simplified. For cases with fragments due
// to transparency, and more complex (and slower) resolve accumulation is
// performed.
//
//-----------------------------------------------------------------------------
void
ReferenceRasterizer::DoFragResolve(
RRColor& ResolvedColor, // out: final color for pixel
RRDepth& ResolvedDepth, // out: final depth for pixel
RRFRAGMENT* pFrag, // in: pointer to frag list for pixel
const RRColor& CoveredColor ) // out: color of frontmost fully covered sample
{
DPFM(7,FRAG,(" presort\n")) DPFFrags(pFrag);
//
// reform fragments into sorted (front-to-back) linked list
//
// put first fragment into sortlist
RRFRAGMENT* pFragSort = pFrag;
pFrag = (RRFRAGMENT *)pFrag->pNext;
pFragSort->pNext = NULL;
// track if any non-opaque alphas (used to select resolve routine)
// init value by checking the first one (already in sort list)
BOOL bAnyNonOpaque = ( UINT8(pFragSort->Color.A) < 0xff );
// step thru fragment list and move each fragment into sort list
while ( NULL != pFrag )
{
// check for non-opaque alpha
if ( UINT8(pFrag->Color.A) < 0xff ) { bAnyNonOpaque = TRUE; }
// get ptr to next here so it can be overwritten
RRFRAGMENT* pFragNext = (RRFRAGMENT *)pFrag->pNext;
// use this to step thru sort list and insert
RRFRAGMENT **ppFragSortT = &pFragSort;
while ( NULL != *ppFragSortT )
{
if ( DepthCloser( pFrag->Depth, (*ppFragSortT)->Depth ) )
{
// current frag is closer than sort list entry, so
// before this sort entry
pFrag->pNext = *ppFragSortT;
*ppFragSortT = pFrag;
break;
}
else if ( NULL == (*ppFragSortT)->pNext )
{
// if last, then insert after this sort list entry
(*ppFragSortT)->pNext = pFrag;
pFrag->pNext = NULL;
break;
}
ppFragSortT = (RRFRAGMENT **)&((*ppFragSortT)->pNext);
}
// advance input frag list
pFrag = pFragNext;
}
// all fragments have now been passed to sort list in front-to-back order
DPFM(7,FRAG,(" postsort\n")) DPFFrags(pFragSort);
// return first sorted fragment (this is the closest, which is as good
// as anything to put into the depth buffer for the resolved pixels...)
ResolvedDepth = pFragSort->Depth;
//
// now step thru sorted list and accumulate color
//
if ( bAnyNonOpaque )
{
// here for fragment resolve accumulation which also does
// full transparency computations - use this only if any
// non-opaque fragments
// instantiate and reset fragment resolve accumulator
FragResolveAccum ResAccum;
ResAccum.Reset();
// per frag
pFrag = pFragSort;
BOOL bCovered = FALSE;
while ( NULL != pFrag )
{
bCovered = ResAccum.Accum( pFrag->CvgMask, pFrag->Color );
if (bCovered) { break; } // fully covered, so don't do rest of frags (or background)
pFrag = (RRFRAGMENT *)pFrag->pNext;
}
// add in background color (last)
if ( !bCovered && ( UINT8(CoveredColor.A) > 0 ) )
{
ResAccum.Accum( TL_CVGFULL, CoveredColor );
}
// unload accumulator
ResAccum.GetColor( ResolvedColor );
}
else
{
//
// here for fragment resolve of all fully opaque fragments
//
//
// accumulated coverage and color
//
CVGMASK CvgMaskAccum = 0x0;
FLOAT fRAcc = 0.F; // these 0. to 1. range
FLOAT fGAcc = 0.F;
FLOAT fBAcc = 0.F;
FLOAT fWeightAccum = 0.F;
// per frag
pFrag = pFragSort;
while ( NULL != pFrag )
{
// compute this fragment's contribution
CVGMASK CvgMaskContrib = pFrag->CvgMask & ~(CvgMaskAccum);
FLOAT fWeight = (1.f/16.f) * (FLOAT)CountSetBits(CvgMaskContrib, 16);
// accumulate rgb
fRAcc += fWeight * FLOAT(pFrag->Color.R);
fGAcc += fWeight * FLOAT(pFrag->Color.G);
fBAcc += fWeight * FLOAT(pFrag->Color.B);
// accumulate total coverage and weight
CvgMaskAccum |= CvgMaskContrib;
fWeightAccum += fWeight;
// bail out early if fully covered
if ( TL_CVGFULL == CvgMaskAccum ) { goto DoneAccumulating; }
// next
pFrag = (RRFRAGMENT *)pFrag->pNext;
}
// blend with background color/alpha
if ( (fWeightAccum < 1.f) && ( UINT8(CoveredColor.A) > 0 ) )
{
// blend in remaining weight of background color
FLOAT fWeightBg = 1.F - fWeightAccum;
fRAcc += fWeightBg * FLOAT(CoveredColor.R);
fGAcc += fWeightBg * FLOAT(CoveredColor.G);
fBAcc += fWeightBg * FLOAT(CoveredColor.B);
// fix accumulated weight - pixel is fully covered now
fWeightAccum = 1.f;
}
DoneAccumulating:
// clamp accumulators
if ( fWeightAccum > 1.F ) { fWeightAccum = 1.F; }
if ( fRAcc > 1.F ) { fRAcc = 1.F; }
if ( fGAcc > 1.F ) { fGAcc = 1.F; }
if ( fBAcc > 1.F ) { fBAcc = 1.F; }
// set in color return
ResolvedColor.A = fWeightAccum;
ResolvedColor.R = fRAcc;
ResolvedColor.G = fGAcc;
ResolvedColor.B = fBAcc;
}
// free this pixel's fragments
pFrag = pFragSort;
while ( NULL != pFrag )
{
RRFRAGMENT* pFragFree = pFrag;
pFrag = (RRFRAGMENT*)pFrag->pNext;
FragFree( pFragFree );
}
return;
}
//-----------------------------------------------------------------------------
//
// DoBufferResolve - Invoked at EndScene to resolve fragments into single
// color for each pixel location.
//
//-----------------------------------------------------------------------------
void
ReferenceRasterizer::DoBufferResolve(void)
{
DPFM(2,FRAG,(" DoBufferResolve (%d,%d)\n",m_pRenderTarget->m_iWidth,m_pRenderTarget->m_iHeight))
// buffer may not be allocated if there were no fragments
if (NULL == m_ppFragBuf) { return; }
for ( int iY=0; iY < m_pRenderTarget->m_iHeight; iY++ )
{
for ( int iX=0; iX < m_pRenderTarget->m_iWidth; iX++ )
{
RRFRAGMENT* pFrag = *(m_ppFragBuf + (m_pRenderTarget->m_iWidth*iY) + iX);
if ( NULL != pFrag )
{
DPFM(5,FRAG,(" DoResolve(%d,%d) %d\n",iX,iY,CountFrags(pFrag)))
// read buffer color for background blend
RRColor PixelColor; m_pRenderTarget->ReadPixelColor( iX,iY, PixelColor);
// do resolve
RRColor ResolvedColor;
RRDepth ResolvedDepth(pFrag->Depth.GetSType());
DoFragResolve( ResolvedColor, ResolvedDepth, pFrag, PixelColor );
// write color back to buffer; write frontmost depth back to
// pixel buffer (it's at least a little better than the backmost
// opaque sample...)
WritePixel( iX,iY, ResolvedColor, ResolvedDepth );
// show frags freed (free happens during resolve)
*(m_ppFragBuf + (m_pRenderTarget->m_iWidth*iY) + iX) = NULL;
}
}
}
DPFM(3,FRAG,(" DoBufferResolve - done\n"))
}
///////////////////////////////////////////////////////////////////////////////
// //
// Fragment Resolve Accumulator //
// //
///////////////////////////////////////////////////////////////////////////////
//
// FragResolveAccum - This object is the fragment resolver used when
// non-opaque fragments are present. This has the effect of resolving
// each of the 16 subpixel locations independently to produce the fully
// correct result. Several optimizations are used to minimize the actual
// number of accumulation computations that need to occur.
//
//-----------------------------------------------------------------------------
//
// Reset - Called prior to resolving a list of fragments to initialize the
// internal state.
//
//-----------------------------------------------------------------------------
void
FragResolveAccum::Reset(void)
{
DPFM(5, FRAG, (" FragResolveAccum: reset\n"))
m_ArrayUsageMask = 0x0001; // use first array entry only (at first)
m_CvgArray[0].Mask = TL_CVGFULL;
m_CvgArray[0].fAlpha = 1.;
m_fA = 0.;
m_fR = 0.;
m_fG = 0.;
m_fB = 0.;
m_CvgOpaqueMask = 0x0000;
}
//-----------------------------------------------------------------------------
//
// Accum - Called for each fragment. Fragments must be accumulated in front-
// to-back order (sort is done prior to accumulation).
//
// Returns TRUE if full coverage has been achieved and thus subsequent
// fragments will have no further contribution to the final pixel color and
// opacity.
//
//-----------------------------------------------------------------------------
BOOL
FragResolveAccum::Accum(
const CVGMASK CvgMask,
const RRColor& ColorFrag)
{
DPFM(6, FRAG, (" FragResolveAccum: accum %04x %08x\n",
CvgMask, UINT32(ColorFrag) ) )
FLOAT fAlphaFrag = FLOAT(ColorFrag.A);
// exit (don't accum) if all covered
if (TL_CVGFULL == m_CvgOpaqueMask) { return TRUE; }
// controls for doing (up to) 4 accumulations at a time
INT32 iAccumsDeferred = 0; // the current number of deferred accumulations
FLOAT fColorScaleAccum; // the accumulated color scale for the deferred accums
// compute ArrayCheck - each set bit indicates a coverage mask
// bit for which an accumulation needs to be done (indicated by
// a valid entry in the coverage array which is not opaque and
// for which the corresponding bit is set in this fragment's
// coverage mask)
CVGMASK ArrayCheck = 0x0;
for (INT32 i=0; i<16; i++)
{
if (m_CvgArray[i].Mask & CvgMask)
{
ArrayCheck |= ((0x1 << i) & ~(m_CvgOpaqueMask));
}
}
INT32 iIdx;
CVGMASK ArrayMaskT = m_ArrayUsageMask;
while (0x0 != ArrayMaskT)
{
// track from MSB to LSB of usage mask
iIdx = FindLastSetBit(ArrayMaskT,TL_CVGBITS);
ArrayMaskT &= ~(0x1 << iIdx);
// compute masks for overlapped coverage (requiring
// accumulation) and non-overlapped area (which may
// require an updated coverage/alpha entry)
CVGMASK AccumCvgMask = m_CvgArray[iIdx].Mask & CvgMask;
CVGMASK UpdateCvgMask = m_CvgArray[iIdx].Mask & ~CvgMask;
// remove bits in the overlapped coverage mask for subsamples
// which already have opaque alphas
AccumCvgMask &= ~(m_CvgOpaqueMask);
// read alpha old here - the location that this is stored
// may be changed in the accumulate step but needs to be
// remembered for the update (non-covered area) step
FLOAT fAlphaOld = m_CvgArray[iIdx].fAlpha;
// compute alpha scale value - this is used to scale color
// for accumulation and to compute updated alpha for overlap
FLOAT fAlphaScale = fAlphaOld * fAlphaFrag;
// new alpha for overlapped area (this cannot go negative
// since 0 < AlphaScale < AlphaOld)
// AlphaNext = AlphaOld(1 - Alpha) = AlphaOld - AlphaOld*Alpha =
FLOAT fAlphaNext = fAlphaOld - fAlphaScale;
if (0x0 != AccumCvgMask)
{
// contribution to accumulate - this is the portion
// the previous mask starting at the uIdx bit location
// which is covered by the new fragment, so accumulate
// this coverage and update the mask and alpha
UINT32 iIdxT = FindFirstSetBit(AccumCvgMask,TL_CVGBITS);
m_ArrayUsageMask |= (0x1 << iIdxT);
m_CvgArray[iIdxT].Mask = AccumCvgMask;
// set the alpha of the overlapped area
m_CvgArray[iIdxT].fAlpha = fAlphaNext;
// compute scale for color channels - depends on if
// we want pre-multiplied alphas or not...
//
// base for scale is either array value alone or product
// of array value and Afrag (AlphaScale)
FLOAT fColorScaleBase = (g_bPreMultAlpha) ? (fAlphaOld) : (fAlphaScale);
// do either multiply or bypass for full coverage
FLOAT fColorScale = fColorScaleBase;
if ( TL_CVGFULL != AccumCvgMask )
{
FLOAT fCvgFraction =
(FLOAT)(CountSetBits(AccumCvgMask, TL_CVGBITS)) * (1./TL_CVGBITS);
fColorScale *= fCvgFraction;
}
// accumulate up to four accumulations to do at once - the accumulated
// value is the color scale to be applied to the multiple locations
// update color scale accum - either set (1st deferral) or
// accumulate (subsequent deferrals)
fColorScaleAccum = (0 == iAccumsDeferred) ?
(fColorScale) : (fColorScale + fColorScaleAccum);
// track number of deferrals and bypass accumulation if not
// up to 4 (or if this is the last one)
if ( (++iAccumsDeferred != 4) &&
(0x0 != (ArrayMaskT & ArrayCheck)) )
{
goto _update_CvgMask_Location;
}
// start over on deferral
iAccumsDeferred = 0;
// clamp color scale to max before accumulation
fColorScale = MIN( fColorScaleAccum, 1. );
// do accumulation and write back to accumulator
// decide what to use for alpha accumulate - if we are using
// pre-multiplied alphas, then AFrag is not incorporated into
// color scale, thus mult by AFrag
FLOAT fAPartial = fColorScale * ( (g_bPreMultAlpha) ? (fAlphaFrag) : (1.) );
FLOAT fRPartial = fColorScale * FLOAT(ColorFrag.R);
FLOAT fGPartial = fColorScale * FLOAT(ColorFrag.G);
FLOAT fBPartial = fColorScale * FLOAT(ColorFrag.B);
m_fA += fAPartial;
m_fR += fRPartial;
m_fG += fGPartial;
m_fB += fBPartial;
}
_update_CvgMask_Location:
if (0x0 != UpdateCvgMask)
{
// mask to update - this is the portion of the
// previous mask starting at the uIdx bit location
// which is still visible, so update the coverage
// (the alpha stays the same)
UINT32 iIdxT = FindFirstSetBit(UpdateCvgMask,TL_CVGBITS);
m_ArrayUsageMask |= (0x1 << iIdxT);
m_CvgArray[iIdxT].Mask = UpdateCvgMask;
m_CvgArray[iIdxT].fAlpha = fAlphaOld;
}
}
// determine if this new fragment is has an opaque alpha
// if so then update opaque mask - this must be done after
// the accumulations because the opaque mask refers to the
// current state of the coverage array and should apply only to
// accumulations of subsequent fragments
//
// g_bDoCoverageOnly overrides this to always act as if fragments'
// alphas are opaque for the purposes of generating antialiased
// shadow attenuation surfaces
{
if ((fAlphaFrag >= 1.) || (g_bDoCoverageOnly))
{ m_CvgOpaqueMask |= CvgMask; }
}
// check opaque mask for return boolean - returns TRUE if we
// are done
return (TL_CVGFULL == m_CvgOpaqueMask) ? (TRUE) : (FALSE);
}
//-----------------------------------------------------------------------------
//
// GetColor - Called after accumulating a series of fragments to get the final
// pixel color and alpha.
//
//-----------------------------------------------------------------------------
void
FragResolveAccum::GetColor( RRColor& Color )
{
// clamp and assign for return
Color.A = (FLOAT)MIN( m_fA, 1. );
Color.R = (FLOAT)MIN( m_fR, 1. );
Color.G = (FLOAT)MIN( m_fG, 1. );
Color.B = (FLOAT)MIN( m_fB, 1. );
}
///////////////////////////////////////////////////////////////////////////////
// end