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