windows-nt/Source/XPSP1/NT/multimedia/directx/dxg/ref8/rast/pixproc.cpp
2020-09-26 16:20:57 +08:00

608 lines
21 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 2000.
//
// pixproc.cpp
//
// Direct3D Reference Device - Pixel Processor
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop
//-----------------------------------------------------------------------------
//
// WritePixel - Writes pixel and (maybe) depth to current render target.
//
//-----------------------------------------------------------------------------
void
RefRast::WritePixel(
INT32 iX, INT32 iY, UINT Sample,
const RDColor& Color, const RDDepth& Depth)
{
m_pRD->m_pRenderTarget->WritePixelColor( iX, iY, Sample, Color,
m_pRD->GetRS()[D3DRS_DITHERENABLE]);
// don't write if Z buffering disabled or Z write disabled
if ( !( m_pRD->GetRS()[D3DRS_ZENABLE ] ) ||
!( m_pRD->GetRS()[D3DRS_ZWRITEENABLE] ) ) { return; }
m_pRD->m_pRenderTarget->WritePixelDepth( iX, iY, Sample, Depth );
}
//-----------------------------------------------------------------------------
//
// DoPixels - Invoked for each set of 2x2 pixels by the scan converter, applies
// texture, specular, fog, alpha blend, and writes result to surface. Also
// implements depth, alpha, and stencil tests.
//
//-----------------------------------------------------------------------------
void
RefRast::DoPixels( void )
{
#if DBG
for ( m_iPix = 0; m_iPix < 4; m_iPix++ )
{
if ( !m_bPixelIn[m_iPix] ) continue;
if (m_pRD->m_pDbgMon) m_pRD->m_pDbgMon->NextEvent( D3DDM_EVENT_PIXEL );
}
#endif
// pixel shader executed for all 4 pixels of 2x2 grid at one time
if (m_pCurrentPixelShader)
ExecShader();
for ( m_iPix = 0; m_iPix < 4; m_iPix++ )
{
if ( !m_bPixelIn[m_iPix] ) continue;
if ( m_bPixelDiscard[m_iPix] ) continue;
RDColor PixelColor;
if ( !m_bLegacyPixelShade )
{
// pixel shader final color always left in temp register 0
PixelColor = m_TempReg[0][m_iPix];
// saturate before blend and FB access
PixelColor.Clamp();
}
else
{
// apply legacy pixel shading (texture lookups already done by ExecShader)
PixelColor = m_InputReg[0][m_iPix];
RDColor PixelSpecular( m_InputReg[1][m_iPix] );
RDColor LastStageColor( PixelColor );
RDColor ResultColor( PixelColor );
RDColor TempColor( (DWORD)0x0 );
for ( int iStage=0; iStage<m_pRD->m_cActiveTextureStages; iStage++ )
{
if ( m_pRD->GetTSS(iStage)[D3DTSS_COLOROP] == D3DTOP_DISABLE )
{
ResultColor = LastStageColor; // pass result of previous stage
break;
}
// no blend if texture bound to stage is bumpmap
if ( ( m_pRD->GetTSS(iStage)[D3DTSS_COLOROP] == D3DTOP_BUMPENVMAP ) ||
( m_pRD->GetTSS(iStage)[D3DTSS_COLOROP] == D3DTOP_BUMPENVMAPLUMINANCE ) )
{
continue;
}
RDColor TextureColor( m_TextReg[iStage][m_iPix] );
DoTextureBlendStage( iStage, PixelColor, PixelSpecular,
LastStageColor, TextureColor, TempColor, ResultColor );
// set color for next stage
LastStageColor = ResultColor;
}
PixelColor = ResultColor;
// add specular and saturate
if ( m_pRD->GetRS()[D3DRS_SPECULARENABLE] )
{
PixelColor.R += PixelSpecular.R;
PixelColor.G += PixelSpecular.G;
PixelColor.B += PixelSpecular.B;
PixelColor.Saturate();
}
}
// do alpha test - bail out if failed
if ( m_pRD->GetRS()[D3DRS_ALPHATESTENABLE] &&
!AlphaTest( PixelColor.A ) )
{
continue;
}
// apply fog
if ( m_pRD->GetRS()[D3DRS_FOGENABLE] )
{
RDColor FogColor = m_pRD->GetRS()[D3DRS_FOGCOLOR];
// (TODO: account for pre-multiplied alpha here??)
FLOAT ObjColorFrac = m_FogIntensity[m_iPix];
FLOAT FogColorFrac = 1.f - m_FogIntensity[m_iPix];
PixelColor.R = (ObjColorFrac * PixelColor.R) + (FogColorFrac * FogColor.R);
PixelColor.G = (ObjColorFrac * PixelColor.G) + (FogColorFrac * FogColor.G);
PixelColor.B = (ObjColorFrac * PixelColor.B) + (FogColorFrac * FogColor.B);
}
//
// remainder is done per-sample for multisample buffers
//
INT32 iX = m_iX[m_iPix];
INT32 iY = m_iY[m_iPix];
do
{
RDColor FinalPixelColor = PixelColor;
UINT iSample = GetCurrentSample();
if ( !m_bIsLine &&
( !GetCurrentSampleMask() ||
!m_bSampleCovered[iSample][m_iPix] ) )
{
// iSample not covered by this geometry
continue;
}
//
// read current depth for this pixel and do depth test - cannot
// bail out if failed because stencil may need to be updated
//
BOOL bDepthTestPassed = TRUE;
if ( m_pRD->GetRS()[D3DRS_ZENABLE] )
{
m_Depth[m_iPix] = m_SampleDepth[iSample][m_iPix];
RDDepth BufferDepth( m_Depth[m_iPix].GetSType() );
m_pRD->m_pRenderTarget->ReadPixelDepth( iX, iY, iSample, BufferDepth );
bDepthTestPassed = DepthCloser( m_Depth[m_iPix], BufferDepth );
}
//
// do stencil operation
//
BOOL bStencilTestPassed = TRUE;
if ( m_pRD->GetRS()[D3DRS_STENCILENABLE] )
{
// read stencil buffer and do stencil operation
UINT8 uStncBuf = 0x0;
m_pRD->m_pRenderTarget->ReadPixelStencil( iX, iY, iSample, uStncBuf );
UINT8 uStncNew;
bStencilTestPassed =
DoStencil( uStncBuf, bDepthTestPassed, m_pRD->m_pRenderTarget->m_pDepth->GetSurfaceFormat(), uStncNew );
// update stencil only if changed
if ( uStncNew != uStncBuf )
{
// compute new buffer value based on write mask
UINT8 uStncWMask = m_pRD->GetRS()[D3DRS_STENCILWRITEMASK];
UINT8 uStncBufNew = (uStncBuf & ~uStncWMask) | (uStncNew & uStncWMask);
m_pRD->m_pRenderTarget->WritePixelStencil( iX, iY, iSample, uStncBufNew );
}
}
if ( !(bDepthTestPassed && bStencilTestPassed) )
{
continue;
}
//
// do alpha blend and write mask
//
if ( ( ( m_pRD->GetRS()[D3DRS_COLORWRITEENABLE] & 0xF) != 0xF ) ||
( m_pRD->GetRS()[D3DRS_ALPHABLENDENABLE] ) )
{
RDColor BufferColor;
m_pRD->m_pRenderTarget->ReadPixelColor( iX, iY, iSample, BufferColor );
if ( m_pRD->GetRS()[D3DRS_ALPHABLENDENABLE] )
{
DoAlphaBlend( FinalPixelColor, BufferColor, FinalPixelColor );
}
if ( !(m_pRD->GetRS()[D3DRS_COLORWRITEENABLE] & D3DCOLORWRITEENABLE_RED) )
FinalPixelColor.R = BufferColor.R;
if ( !(m_pRD->GetRS()[D3DRS_COLORWRITEENABLE] & D3DCOLORWRITEENABLE_GREEN) )
FinalPixelColor.G = BufferColor.G;
if ( !(m_pRD->GetRS()[D3DRS_COLORWRITEENABLE] & D3DCOLORWRITEENABLE_BLUE) )
FinalPixelColor.B = BufferColor.B;
if ( !(m_pRD->GetRS()[D3DRS_COLORWRITEENABLE] & D3DCOLORWRITEENABLE_ALPHA) )
FinalPixelColor.A = BufferColor.A;
}
#if 0
{
extern float g_GammaTable[];
FinalPixelColor.R = g_GammaTable[ (UINT8)(255.f*FinalPixelColor.R) ];
FinalPixelColor.G = g_GammaTable[ (UINT8)(255.f*FinalPixelColor.G) ];
FinalPixelColor.B = g_GammaTable[ (UINT8)(255.f*FinalPixelColor.B) ];
}
#endif
//
// update color and depth buffers
//
WritePixel( iX, iY, iSample, FinalPixelColor, m_Depth[m_iPix] );
} while (NextSample());
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// Pixel Processing Utility Functions //
// //
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Depth compare method used for Z buffering and fragment processing.
//
// Returns TRUE if DepthVal is closer than DepthBuf. DepthA is the generated
// value and DepthB
//
//-----------------------------------------------------------------------------
BOOL
RefRast::DepthCloser(
const RDDepth& DepthVal,
const RDDepth& DepthBuf )
{
if ( !m_pRD->GetRS()[D3DRS_ZENABLE] ) { return TRUE; }
switch ( m_pRD->GetRS()[D3DRS_ZFUNC] )
{
case D3DCMP_NEVER: return FALSE;
case D3DCMP_LESS: return ( DOUBLE(DepthVal) < DOUBLE(DepthBuf) );
case D3DCMP_EQUAL: return ( DOUBLE(DepthVal) == DOUBLE(DepthBuf) );
case D3DCMP_LESSEQUAL: return ( DOUBLE(DepthVal) <= DOUBLE(DepthBuf) );
case D3DCMP_GREATER: return ( DOUBLE(DepthVal) > DOUBLE(DepthBuf) );
case D3DCMP_NOTEQUAL: return ( DOUBLE(DepthVal) != DOUBLE(DepthBuf) );
case D3DCMP_GREATEREQUAL: return ( DOUBLE(DepthVal) >= DOUBLE(DepthBuf) );
case D3DCMP_ALWAYS: return TRUE;
}
return TRUE;
}
//-----------------------------------------------------------------------------
//
// Alpha test method for pixel processing.
//
// Returns TRUE if alpha test passes.
//
//-----------------------------------------------------------------------------
BOOL
RefRast::AlphaTest( FLOAT fAlpha )
{
// grab 8 bit unsigned alpha value
UINT8 uAlpha = (UINT8)(255.f*fAlpha);
// form 8 bit alpha reference value
UINT8 uAlphaRef8 = m_pRD->GetRS()[D3DRS_ALPHAREF];
// do alpha test and either return directly or pass through
switch ( m_pRD->GetRS()[D3DRS_ALPHAFUNC] )
{
case D3DCMP_NEVER: return FALSE;
case D3DCMP_LESS: return (uAlpha < uAlphaRef8);
case D3DCMP_EQUAL: return (uAlpha == uAlphaRef8);
case D3DCMP_LESSEQUAL: return (uAlpha <= uAlphaRef8);
case D3DCMP_GREATER: return (uAlpha > uAlphaRef8);
case D3DCMP_NOTEQUAL: return (uAlpha != uAlphaRef8);
case D3DCMP_GREATEREQUAL: return (uAlpha >= uAlphaRef8);
case D3DCMP_ALWAYS: return TRUE;
}
return TRUE;
}
//-----------------------------------------------------------------------------
//
// DoStencil - Performs stencil test. Returns TRUE if stencil test passed.
// Also computes stencil result value (to be written back to stencil planes
// if test passes, subject to stencil write mask).
//
//-----------------------------------------------------------------------------
BOOL
RefRast::DoStencil(
UINT8 uStncBuf, // in: stencil buffer value
BOOL bDepthTest, // in: boolean result of depth test
RDSurfaceFormat DepthSType, // in: surface type of Z buffer
UINT8& uStncRet) // out: stencil value result
{
// support 8 bit stencil only, so do everything as UINT8's
// max value for masking and saturation ops
UINT8 uStncMax;
switch(DepthSType)
{
case RD_SF_Z24S8:
case RD_SF_S8Z24: uStncMax = 0xff; break;
case RD_SF_Z15S1:
case RD_SF_S1Z15: uStncMax = 0x1; break;
case RD_SF_Z24X4S4:
case RD_SF_X4S4Z24: uStncMax = 0xf; break;
default: uStncMax = 0; break; // don't let stencil become non 0
}
// get reference from renderstate
UINT8 uStncRef = (UINT8)(m_pRD->GetRS()[D3DRS_STENCILREF]);
// mask to use only bits possibly present in stencil buffer
uStncRef &= uStncMax;
// form masked values for test
UINT8 uStncMask = (UINT8)(m_pRD->GetRS()[D3DRS_STENCILMASK]);
UINT8 uStncBufM = uStncBuf & uStncMask;
UINT8 uStncRefM = uStncRef & uStncMask;
// do stencil compare function
BOOL bStncTest = FALSE;
switch ( m_pRD->GetRS()[D3DRS_STENCILFUNC] )
{
case D3DCMP_NEVER: bStncTest = FALSE; break;
case D3DCMP_LESS: bStncTest = (uStncRefM < uStncBufM); break;
case D3DCMP_EQUAL: bStncTest = (uStncRefM == uStncBufM); break;
case D3DCMP_LESSEQUAL: bStncTest = (uStncRefM <= uStncBufM); break;
case D3DCMP_GREATER: bStncTest = (uStncRefM > uStncBufM); break;
case D3DCMP_NOTEQUAL: bStncTest = (uStncRefM != uStncBufM); break;
case D3DCMP_GREATEREQUAL: bStncTest = (uStncRefM >= uStncBufM); break;
case D3DCMP_ALWAYS: bStncTest = TRUE; break;
}
// determine which stencil operation to perform
DWORD dwStencilOp;
if ( !bStncTest )
{
// stencil test failed - depth test does not matter
dwStencilOp = m_pRD->GetRS()[D3DRS_STENCILFAIL];
}
else
{
// stencil test passed - select based on depth pass/fail
dwStencilOp = ( !bDepthTest )
? ( m_pRD->GetRS()[D3DRS_STENCILZFAIL] )
: ( m_pRD->GetRS()[D3DRS_STENCILPASS] );
}
uStncRet = 0x0;
switch ( dwStencilOp )
{
case D3DSTENCILOP_KEEP: uStncRet = uStncBuf; break;
case D3DSTENCILOP_ZERO: uStncRet = 0x00; break;
case D3DSTENCILOP_REPLACE: uStncRet = uStncRef; break;
case D3DSTENCILOP_INCRSAT:
uStncRet = (uStncBuf==uStncMax)?(uStncMax):(uStncBuf+1); break;
case D3DSTENCILOP_DECRSAT:
uStncRet = (uStncBuf==0x00)?(0x00):(uStncBuf-1); break;
case D3DSTENCILOP_INVERT: uStncRet = ~uStncBuf; break;
case D3DSTENCILOP_INCR: uStncRet = uStncBuf+1; break;
case D3DSTENCILOP_DECR: uStncRet = uStncBuf-1; break;
}
return bStncTest;
}
//-----------------------------------------------------------------------------
//
// DoAlphaBlend - Performs color blending of source and destination colors
// producing a result color.
//
//-----------------------------------------------------------------------------
void
RefRast::DoAlphaBlend(
const RDColor& SrcColor, // in: source pixel color
const RDColor& DstColor, // in: destination (buffer) color
RDColor& ResColor) // out: result (blended) color
{
RDColor SrcColorFactor;
RDColor DstColorFactor;
BOOL bDestBlendOverride = FALSE;
// no SRC/DST blend (or clamp) required for MIN or MAX BLENDOP
switch ( m_pRD->GetRS()[D3DRS_BLENDOP] )
{
case D3DBLENDOP_MIN:
ResColor.R = MIN(SrcColor.R,DstColor.R);
ResColor.G = MIN(SrcColor.G,DstColor.G);
ResColor.B = MIN(SrcColor.B,DstColor.B);
ResColor.A = MIN(SrcColor.A,DstColor.A);
return;
case D3DBLENDOP_MAX:
ResColor.R = MAX(SrcColor.R,DstColor.R);
ResColor.G = MAX(SrcColor.G,DstColor.G);
ResColor.B = MAX(SrcColor.B,DstColor.B);
ResColor.A = MAX(SrcColor.A,DstColor.A);
return;
}
// compute source blend factors
switch ( m_pRD->GetRS()[D3DRS_SRCBLEND] )
{
default:
case D3DBLEND_ZERO:
SrcColorFactor.SetAllChannels( 0.F );
break;
case D3DBLEND_ONE:
SrcColorFactor.SetAllChannels( 1.F );
break;
case D3DBLEND_SRCCOLOR:
SrcColorFactor.R = SrcColor.R;
SrcColorFactor.G = SrcColor.G;
SrcColorFactor.B = SrcColor.B;
SrcColorFactor.A = SrcColor.A;
break;
case D3DBLEND_INVSRCCOLOR:
SrcColorFactor.R = ( 1.f - SrcColor.R );
SrcColorFactor.G = ( 1.f - SrcColor.G );
SrcColorFactor.B = ( 1.f - SrcColor.B );
SrcColorFactor.A = ( 1.f - SrcColor.A );
break;
case D3DBLEND_SRCALPHA:
SrcColorFactor.SetAllChannels( SrcColor.A );
break;
case D3DBLEND_INVSRCALPHA:
SrcColorFactor.SetAllChannels( 1.f - SrcColor.A );
break;
case D3DBLEND_DESTALPHA:
SrcColorFactor.SetAllChannels( DstColor.A );
break;
case D3DBLEND_INVDESTALPHA:
SrcColorFactor.SetAllChannels( 1.f - DstColor.A );
break;
case D3DBLEND_DESTCOLOR:
SrcColorFactor.R = DstColor.R;
SrcColorFactor.G = DstColor.G;
SrcColorFactor.B = DstColor.B;
SrcColorFactor.A = DstColor.A;
break;
case D3DBLEND_INVDESTCOLOR:
SrcColorFactor.R = ( 1.f - DstColor.R );
SrcColorFactor.G = ( 1.f - DstColor.G );
SrcColorFactor.B = ( 1.f - DstColor.B );
SrcColorFactor.A = ( 1.f - DstColor.A );
break;
case D3DBLEND_SRCALPHASAT:
{
FLOAT F = MIN( SrcColor.A, 1.f - DstColor.A );
SrcColorFactor.R = F;
SrcColorFactor.G = F;
SrcColorFactor.B = F;
}
SrcColorFactor.A = 1.F;
break;
// these are for SRCBLEND only and override DESTBLEND
case D3DBLEND_BOTHSRCALPHA:
bDestBlendOverride = TRUE;
SrcColorFactor.SetAllChannels( SrcColor.A );
DstColorFactor.SetAllChannels( 1.f - SrcColor.A );
break;
case D3DBLEND_BOTHINVSRCALPHA:
bDestBlendOverride = TRUE;
SrcColorFactor.SetAllChannels( 1.f - SrcColor.A );
DstColorFactor.SetAllChannels( SrcColor.A );
break;
}
// compute destination blend factors
if ( !bDestBlendOverride )
{
switch ( m_pRD->GetRS()[D3DRS_DESTBLEND] )
{
default:
case D3DBLEND_ZERO:
DstColorFactor.SetAllChannels( 0.F );
break;
case D3DBLEND_ONE:
DstColorFactor.SetAllChannels( 1.F );
break;
case D3DBLEND_SRCCOLOR:
DstColorFactor.R = SrcColor.R;
DstColorFactor.G = SrcColor.G;
DstColorFactor.B = SrcColor.B;
DstColorFactor.A = SrcColor.A;
break;
case D3DBLEND_INVSRCCOLOR:
DstColorFactor.R = ( 1.f - SrcColor.R );
DstColorFactor.G = ( 1.f - SrcColor.G );
DstColorFactor.B = ( 1.f - SrcColor.B );
DstColorFactor.A = ( 1.f - SrcColor.A );
break;
case D3DBLEND_SRCALPHA:
DstColorFactor.SetAllChannels( SrcColor.A );
break;
case D3DBLEND_INVSRCALPHA:
DstColorFactor.SetAllChannels( 1.f - SrcColor.A );
break;
case D3DBLEND_DESTALPHA:
DstColorFactor.SetAllChannels( DstColor.A );
break;
case D3DBLEND_INVDESTALPHA:
DstColorFactor.SetAllChannels( 1.f - DstColor.A );
break;
case D3DBLEND_DESTCOLOR:
DstColorFactor.R = DstColor.R;
DstColorFactor.G = DstColor.G;
DstColorFactor.B = DstColor.B;
DstColorFactor.A = DstColor.A;
break;
case D3DBLEND_INVDESTCOLOR:
DstColorFactor.R = ( 1.f - DstColor.R );
DstColorFactor.G = ( 1.f - DstColor.G );
DstColorFactor.B = ( 1.f - DstColor.B );
DstColorFactor.A = ( 1.f - DstColor.A );
break;
case D3DBLEND_SRCALPHASAT:
{
FLOAT F = MIN( SrcColor.A, 1.f - DstColor.A );
DstColorFactor.R = F;
DstColorFactor.G = F;
DstColorFactor.B = F;
}
DstColorFactor.A = 1.F;
break;
}
}
// apply blend factors to update pixel color (MIN and MAX handled above)
RDColor SclSrc, SclDst;
SclSrc.R = SrcColorFactor.R * SrcColor.R;
SclSrc.G = SrcColorFactor.G * SrcColor.G;
SclSrc.B = SrcColorFactor.B * SrcColor.B;
SclSrc.A = SrcColorFactor.A * SrcColor.A;
SclDst.R = DstColorFactor.R * DstColor.R;
SclDst.G = DstColorFactor.G * DstColor.G;
SclDst.B = DstColorFactor.B * DstColor.B;
SclDst.A = DstColorFactor.A * DstColor.A;
switch ( m_pRD->GetRS()[D3DRS_BLENDOP] )
{
default:
case D3DBLENDOP_ADD:
ResColor.R = SclSrc.R + SclDst.R;
ResColor.G = SclSrc.G + SclDst.G;
ResColor.B = SclSrc.B + SclDst.B;
ResColor.A = SclSrc.A + SclDst.A;
break;
case D3DBLENDOP_SUBTRACT:
ResColor.R = SclSrc.R - SclDst.R;
ResColor.G = SclSrc.G - SclDst.G;
ResColor.B = SclSrc.B - SclDst.B;
ResColor.A = SclSrc.A - SclDst.A;
break;
case D3DBLENDOP_REVSUBTRACT:
ResColor.R = SclDst.R - SclSrc.R;
ResColor.G = SclDst.G - SclSrc.G;
ResColor.B = SclDst.B - SclSrc.B;
ResColor.A = SclDst.A - SclSrc.A;
break;
}
// clamp result
ResColor.Clamp();
}
///////////////////////////////////////////////////////////////////////////////
// end