/////////////////////////////////////////////////////////////////////////////// // 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; iStagem_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