|
|
///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 1998.
//
// pixproc.cpp
//
// Direct3D Reference Rasterizer - Pixel Processor
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop
//-----------------------------------------------------------------------------
//
// DoPixel - Invoked for each pixel by the scan converter, applies texture,
// specular, fog, alpha blend, and writes result to surface. Also implements
// depth, alpha, and stencil tests.
//
//-----------------------------------------------------------------------------
void ReferenceRasterizer::DoPixel( RRPixel& Pixel ) {
// apply texture (includes lookup&filter and blending)
if ( m_cActiveTextureStages > 0 ) { RRColor TexturedColor = Pixel.Color; DoTexture( Pixel, TexturedColor ); Pixel.Color = TexturedColor;
// check colorkey
for (INT32 i = 0; i < m_cActiveTextureStages; i++) { if ( NULL != m_pTexture[i] ) { // kill pixel if colorkey killing and any samples matched
if ( m_pTexture[i]->m_bDoColorKeyKill && m_pTexture[i]->m_bColorKeyMatched ) { return; } } } }
// do alpha test - bail out if failed
if ( m_dwRenderState[D3DRENDERSTATE_ALPHATESTENABLE] && !AlphaTest( Pixel.Color.A ) ) { return; }
// add specular and saturate
if ( m_dwRenderState[D3DRENDERSTATE_SPECULARENABLE] ) { Pixel.Color.R += Pixel.Specular.R; Pixel.Color.G += Pixel.Specular.G; Pixel.Color.B += Pixel.Specular.B; Pixel.Color.R = minimum( 1.f, Pixel.Color.R ); Pixel.Color.G = minimum( 1.f, Pixel.Color.G ); Pixel.Color.B = minimum( 1.f, Pixel.Color.B ); }
// apply fog
if ( m_dwRenderState[D3DRENDERSTATE_FOGENABLE] ) { // get RRColor version of fog color
RRColor FogColor = m_dwRenderState[D3DRENDERSTATE_FOGCOLOR];
// do fog blend
// (TODO: account for pre-multiplied alpha here??)
RRColorComp ObjColorFrac = Pixel.FogIntensity; // f
RRColorComp FogColorFrac = ~Pixel.FogIntensity; // 1. - f
Pixel.Color.R = (ObjColorFrac * Pixel.Color.R) + (FogColorFrac * FogColor.R); Pixel.Color.G = (ObjColorFrac * Pixel.Color.G) + (FogColorFrac * FogColor.G); Pixel.Color.B = (ObjColorFrac * Pixel.Color.B) + (FogColorFrac * FogColor.B);
// NOTE: this can be done with a single (signed) multiply as
// (f)*Cp + (1-f)*Cf = f*(Cp-Cf) + Cf
}
//
// read current depth for this pixel and do depth test - cannot
// bail out if failed because stencil may need to be updated
//
RRDepth BufferDepth(Pixel.Depth.GetSType()); BOOL bDepthTestPassed = TRUE; if ( m_dwRenderState[D3DRENDERSTATE_ZENABLE] ) { m_pRenderTarget->ReadPixelDepth( Pixel.iX, Pixel.iY, BufferDepth ); bDepthTestPassed = DepthCloser( Pixel.Depth, BufferDepth ); }
//
// do stencil operation
//
BOOL bStencilTestPassed = TRUE; if ( m_dwRenderState[D3DRENDERSTATE_STENCILENABLE] ) { // read stencil buffer and do stencil operation
UINT8 uStncBuf = 0x0; m_pRenderTarget->ReadPixelStencil( Pixel.iX, Pixel.iY, uStncBuf ); UINT8 uStncNew; bStencilTestPassed = DoStencil( uStncBuf, bDepthTestPassed, Pixel.Depth.GetSType(), uStncNew );
// update stencil only if changed
if ( uStncNew != uStncBuf ) { // compute new buffer value based on write mask
UINT8 uStncWMask = m_dwRenderState[D3DRENDERSTATE_STENCILWRITEMASK]; UINT8 uStncBufNew = (uStncBuf & ~uStncWMask) | (uStncNew & uStncWMask); m_pRenderTarget->WritePixelStencil( Pixel.iX, Pixel.iY, uStncBufNew ); } }
if ( !(bDepthTestPassed && bStencilTestPassed) ) { return; }
//
// do fragment generation processing - this is done prior to alpha blend
// somewhat arbitrarily because fragment generation and incremental alpha
// blending are mutually exclusive (blending of fragments requires multipass
// and fragment matching to get the correct result - fragment matching is
// not implemented here yet) (TODO: fragment matching)
//
// this may or may not complete the processing of this pixel
//
if ( m_bFragmentProcessingEnabled ) { if ( DoFragmentGenerationProcessing( Pixel ) ) { return; } }
//
// do alpha blend
//
if ( m_dwRenderState[D3DRENDERSTATE_ALPHABLENDENABLE] ) { RRColor BufferColor; m_pRenderTarget->ReadPixelColor( Pixel.iX, Pixel.iY, BufferColor ); DoAlphaBlend( Pixel.Color, BufferColor, Pixel.Color ); }
//
// update color and depth buffers
//
WritePixel( Pixel.iX, Pixel.iY, Pixel.Color, Pixel.Depth );
// additional fragment processing associated with buffer write
if ( m_bFragmentProcessingEnabled ) { DoFragmentBufferFixup( Pixel ); } }
///////////////////////////////////////////////////////////////////////////////
// //
// 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 ReferenceRasterizer::DepthCloser( const RRDepth& DepthVal, const RRDepth& DepthBuf ) { if ( !m_dwRenderState[D3DRENDERSTATE_ZENABLE] ) { return TRUE; }
switch ( m_dwRenderState[D3DRENDERSTATE_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 ReferenceRasterizer::AlphaTest( const RRColorComp& Alpha ) { // grab 8 bit unsigned alpha value
UINT8 uAlpha = UINT8( Alpha );
// form 8 bit alpha reference value
UINT8 uAlphaRef8 = m_dwRenderState[D3DRENDERSTATE_ALPHAREF];
// do alpha test and either return directly or pass through
switch ( m_dwRenderState[D3DRENDERSTATE_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 ReferenceRasterizer::DoStencil( UINT8 uStncBuf, // in: stencil buffer value
BOOL bDepthTest, // in: boolean result of depth test
RRSurfaceType DepthSType, // in: surface type of Z buffer
UINT8& uStncRet) // out: stencil value result
{ // support 8 bit stencil only, so do everything as UINT8's
// get reference from renderstate
UINT8 uStncRef = (UINT8)(m_dwRenderState[D3DRENDERSTATE_STENCILREF]);
// form masked values for test
UINT8 uStncMask = (UINT8)(m_dwRenderState[D3DRENDERSTATE_STENCILMASK]); UINT8 uStncBufM = uStncBuf & uStncMask; UINT8 uStncRefM = uStncRef & uStncMask; // max value for saturation ops
UINT8 uStncMax; switch(DepthSType) { case RR_STYPE_Z24S8: case RR_STYPE_S8Z24: uStncMax = 0xff; break; case RR_STYPE_Z15S1: case RR_STYPE_S1Z15: uStncMax = 0x1; break; case RR_STYPE_Z24S4: case RR_STYPE_S4Z24: uStncMax = 0xf; break; default: uStncMax = 0; break; // don't let stencil become non 0
}
// do stencil compare function
BOOL bStncTest = FALSE; switch ( m_dwRenderState[D3DRENDERSTATE_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_dwRenderState[D3DRENDERSTATE_STENCILFAIL]; } else { // stencil test passed - select based on depth pass/fail
dwStencilOp = ( !bDepthTest ) ? ( m_dwRenderState[D3DRENDERSTATE_STENCILZFAIL] ) : ( m_dwRenderState[D3DRENDERSTATE_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 ReferenceRasterizer::DoAlphaBlend( const RRColor& SrcColor, // in: source pixel color
const RRColor& DstColor, // in: destination (buffer) color
RRColor& ResColor) // out: result (blended) color
{ RRColor SrcColorFactor; RRColor DstColorFactor; BOOL bDestBlendOverride = FALSE;
// compute source blend factors
switch ( m_dwRenderState[D3DRENDERSTATE_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 = ~SrcColor.R; SrcColorFactor.G = ~SrcColor.G; SrcColorFactor.B = ~SrcColor.B; SrcColorFactor.A = ~SrcColor.A; break;
case D3DBLEND_SRCALPHA: SrcColorFactor.SetAllChannels( SrcColor.A ); break;
case D3DBLEND_INVSRCALPHA: SrcColorFactor.SetAllChannels( ~SrcColor.A ); break;
case D3DBLEND_DESTALPHA: SrcColorFactor.SetAllChannels( DstColor.A ); break;
case D3DBLEND_INVDESTALPHA: SrcColorFactor.SetAllChannels( ~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 = ~DstColor.R; SrcColorFactor.G = ~DstColor.G; SrcColorFactor.B = ~DstColor.B; SrcColorFactor.A = ~DstColor.A; break;
case D3DBLEND_SRCALPHASAT: { RRColorComp F = minimum( SrcColor.A, ~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( ~SrcColor.A ); break;
case D3DBLEND_BOTHINVSRCALPHA: bDestBlendOverride = TRUE; SrcColorFactor.SetAllChannels( ~SrcColor.A ); DstColorFactor.SetAllChannels( SrcColor.A ); break; }
// compute destination blend factors
if ( !bDestBlendOverride ) { switch ( m_dwRenderState[D3DRENDERSTATE_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 = ~SrcColor.R; DstColorFactor.G = ~SrcColor.G; DstColorFactor.B = ~SrcColor.B; DstColorFactor.A = ~SrcColor.A; break;
case D3DBLEND_SRCALPHA: DstColorFactor.SetAllChannels( SrcColor.A ); break;
case D3DBLEND_INVSRCALPHA: DstColorFactor.SetAllChannels( ~SrcColor.A ); break;
case D3DBLEND_DESTALPHA: DstColorFactor.SetAllChannels( DstColor.A ); break;
case D3DBLEND_INVDESTALPHA: DstColorFactor.SetAllChannels( ~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 = ~DstColor.R; DstColorFactor.G = ~DstColor.G; DstColorFactor.B = ~DstColor.B; DstColorFactor.A = ~DstColor.A; break;
case D3DBLEND_SRCALPHASAT: { RRColorComp F = minimum( SrcColor.A, ~DstColor.A ); DstColorFactor.R = F; DstColorFactor.G = F; DstColorFactor.B = F; } DstColorFactor.A = 1.F; break; } }
// apply blend factors to update pixel color
ResColor.R = (SrcColorFactor.R * SrcColor.R) + (DstColorFactor.R * DstColor.R); ResColor.G = (SrcColorFactor.G * SrcColor.G) + (DstColorFactor.G * DstColor.G); ResColor.B = (SrcColorFactor.B * SrcColor.B) + (DstColorFactor.B * DstColor.B); ResColor.A = (SrcColorFactor.A * SrcColor.A) + (DstColorFactor.A * DstColor.A);
// clamp result
ResColor.R = minimum( 1.f, maximum( 0.f, ResColor.R ) ); ResColor.G = minimum( 1.f, maximum( 0.f, ResColor.G ) ); ResColor.B = minimum( 1.f, maximum( 0.f, ResColor.B ) ); ResColor.A = minimum( 1.f, maximum( 0.f, ResColor.A ) ); }
///////////////////////////////////////////////////////////////////////////////
// end
|