/////////////////////////////////////////////////////////////////////////////// // Copyright (C) Microsoft Corporation, 2000. // // rastprim.cpp // // Direct3D Reference Device - Rasterizer Primitive Routines // /////////////////////////////////////////////////////////////////////////////// #include "pch.cpp" #pragma hdrstop //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- RefRast::~RefRast() { delete m_pLegacyPixelShader; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void RefRast::Init( RefDev* pRD ) { m_pRD = pRD; m_bIsLine = FALSE; m_iFlatVtx = 0; // initialize attributes xD Persp Clamp m_Attr[RDATTR_DEPTH ].Init( this, 1, FALSE, TRUE ); m_Attr[RDATTR_FOG ].Init( this, 1, TRUE, TRUE ); m_Attr[RDATTR_COLOR ].Init( this, 4, TRUE, TRUE ); m_Attr[RDATTR_SPECULAR].Init( this, 4, TRUE, TRUE ); m_Attr[RDATTR_TEXTURE0].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE1].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE2].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE3].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE4].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE5].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE6].Init( this, 4, TRUE, FALSE ); m_Attr[RDATTR_TEXTURE7].Init( this, 4, TRUE, FALSE ); m_iPix = 0; memset( m_bPixelIn, 0, sizeof(m_bPixelIn) ); memset( m_bSampleCovered, 0, sizeof(m_bSampleCovered) ); m_bLegacyPixelShade = TRUE; m_pCurrentPixelShader = NULL; m_CurrentPSInst = 0; #if DBG { DWORD v = 0; if( GetD3DRefRegValue(REG_DWORD, "VerboseCreatePixelShader", &v, sizeof(DWORD)) && v != 0 ) m_bDebugPrintTranslatedPixelShaderTokens = TRUE; else m_bDebugPrintTranslatedPixelShaderTokens = FALSE; } #endif // default value registers UINT i, j; for( i = 0 ; i < 4; i++ ) { for( j = 0; j < 4; j++ ) { m_ZeroReg[i][j] = 0.0f; m_OneReg[i][j] = 1.0f; m_TwoReg[i][j] = 2.0f; } } m_bLegacyPixelShade = TRUE; m_pLegacyPixelShader = NULL; memset( m_bPixelDiscard, 0, sizeof(m_bPixelDiscard) ); // multi-sample stuff m_CurrentSample = 0; m_SampleMask = 0xffffffff; SetSampleMode( 1, TRUE ); m_bSampleCovered[0][0] = m_bSampleCovered[0][1] = m_bSampleCovered[0][2] = m_bSampleCovered[0][3] = TRUE; memset( m_TexCvg, 0, sizeof(m_TexCvg) ); memset( m_TexFlt, 0, sizeof(m_TexFlt) ); } //----------------------------------------------------------------------------- // // SampleAndInvertRHW - Sample 1/W at current given location, invert, return // //----------------------------------------------------------------------------- FLOAT RefRast::SampleAndInvertRHW( FLOAT fX, FLOAT fY ) { FLOAT fPixelRHW = fX*m_fRHWA + fY*m_fRHWB + m_fRHWC; FLOAT fPixelW = ( 0. != fPixelRHW ) ? ( 1./fPixelRHW ) : ( 0. ); return fPixelW; } //----------------------------------------------------------------------------- // // //----------------------------------------------------------------------------- BOOL RefRast::EvalPixelPosition( int iPix ) { BOOL bPixelIn; if (m_SampleCount > 1) { bPixelIn = FALSE; // assume out, then set if any in // generating multiple samples, so must evaluate all // sample positions for in/out do { BOOL bPixelSampleIn = GetCurrentSampleMask(); if (!bPixelSampleIn) continue; // get sample location INT32 iX = GetCurrentSampleX(iPix); INT32 iY = GetCurrentSampleY(iPix); // test each edge for ( int iEdge=0; iEdge 0 ) { return TRUE; } break; case D3DCULL_CCW: if ( m_iDet < 0 ) { return TRUE; } break; } // compute bounding box for scan area FLOAT fXMin = MIN( fX0, MIN( fX1, fX2 ) ); FLOAT fXMax = MAX( fX0, MAX( fX1, fX2 ) ); FLOAT fYMin = MIN( fY0, MIN( fY1, fY2 ) ); FLOAT fYMax = MAX( fY0, MAX( fY1, fY2 ) ); // convert to integer (round to +inf) m_iXMin = (INT32)(fXMin+.5); m_iXMax = (INT32)(fXMax+.5); m_iYMin = (INT32)(fYMin+.5); m_iYMax = (INT32)(fYMax+.5); // clip bbox to rendering surface m_iXMin = MAX( m_iXMin, pClip->left ); m_iXMax = MIN( m_iXMax, pClip->right ); m_iYMin = MAX( m_iYMin, pClip->top ); m_iYMax = MIN( m_iYMax, pClip->bottom ); // reject if no coverage if ( ( m_iXMin < pClip->left ) || ( m_iXMax > pClip->right ) || ( m_iYMin < pClip->top ) || ( m_iYMax > pClip->bottom ) ) { return TRUE; } // compute float versions of snapped coord data m_fX0 = (FLOAT)m_iX0 * 1.0F/16.0F; m_fY0 = (FLOAT)m_iY0 * 1.0F/16.0F; m_fDelX10 = (FLOAT)iDelX10 * 1.0F/16.0F; m_fDelX02 = (FLOAT)iDelX02 * 1.0F/16.0F; m_fDelY01 = (FLOAT)iDelY01 * 1.0F/16.0F; m_fDelY20 = (FLOAT)iDelY20 * 1.0F/16.0F; // compute inverse determinant FLOAT fDet = (1./(FLOAT)(1<<8)) * (FLOAT)m_iDet; m_fTriOODet = 1.f/fDet; // compute linear function for 1/W (for perspective correction) m_fRHW0 = *(pVtx0+3); m_fRHW1 = *(pVtx1+3); m_fRHW2 = *(pVtx2+3); // compute linear deltas along two edges FLOAT fDelAttrib10 = m_fRHW1 - m_fRHW0; FLOAT fDelAttrib20 = m_fRHW2 - m_fRHW0; // compute A & B terms (dVdX and dVdY) m_fRHWA = m_fTriOODet * ( fDelAttrib10 * m_fDelY20 + fDelAttrib20 * m_fDelY01 ); m_fRHWB = m_fTriOODet * ( fDelAttrib20 * m_fDelX10 + fDelAttrib10 * m_fDelX02 ); // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv) m_fRHWC = m_fRHW0 - ( m_fRHWA * m_fX0 ) - ( m_fRHWB * m_fY0 ); return FALSE; } /////////////////////////////////////////////////////////////////////////////// // // Line Setup & Evaluate // /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // PointDiamondCheck - Tests if vertex is within diamond of nearest candidate // position. The +.5 (lower-right) tests are used because this is pixel-relative // test - this corresponds to an upper-left test for a vertex-relative position. // //----------------------------------------------------------------------------- static BOOL PointDiamondCheck( INT32 iXFrac, INT32 iYFrac, BOOL bSlopeIsOne, BOOL bSlopeIsPosOne ) { const INT32 iPosHalf = 0x8; const INT32 iNegHalf = -0x8; INT32 iFracAbsSum = labs( iXFrac ) + labs( iYFrac ); // return TRUE if point is in fully-exclusive diamond if ( iFracAbsSum < iPosHalf ) return TRUE; // else return TRUE if diamond is on left or top extreme of point if ( ( iXFrac == ( bSlopeIsPosOne ? iNegHalf : iPosHalf ) ) && ( iYFrac == 0 ) ) return TRUE; if ( ( iYFrac == iPosHalf ) && ( iXFrac == 0 ) ) return TRUE; // return true if slope is one, vertex is on edge, and (other conditions...) if ( bSlopeIsOne && ( iFracAbsSum == iPosHalf ) ) { if ( bSlopeIsPosOne && ( iXFrac < 0 ) && ( iYFrac > 0 ) ) return TRUE; if ( !bSlopeIsPosOne && ( iXFrac > 0 ) && ( iYFrac > 0 ) ) return TRUE; } return FALSE; } //----------------------------------------------------------------------------- // // PerLineSetup - Does per-line setup including scan conversion // // This implements the Grid Intersect Quanization (GIQ) convention (which is // also used in Windows). // // Returns: TRUE if line is discarded; FALSE if line to be drawn // //----------------------------------------------------------------------------- BOOL RefRast::PerLineSetup( FLOAT* pVtx0, FLOAT* pVtx1, BOOL bLastPixel, RECT* pClip) { m_bIsLine = TRUE; FLOAT fX0 = *(pVtx0+0); FLOAT fY0 = *(pVtx0+1); FLOAT fX1 = *(pVtx1+0); FLOAT fY1 = *(pVtx1+1); // compute fixed point x,y coords snapped to n.4 with nearest-even round m_iX0 = FloatToNdot4( fX0 ); m_iY0 = FloatToNdot4( fY0 ); m_iX1 = FloatToNdot4( fX1 ); m_iY1 = FloatToNdot4( fY1 ); // compute x,y extents of the line (fixed point) INT32 iXSize = m_iX1 - m_iX0; INT32 iYSize = m_iY1 - m_iY0; if ( ( iXSize == 0 ) && ( iYSize == 0 ) ) { return TRUE; } // determine major direction and compute line function // use GreaterEqual compare here so X major will be used when slope is // exactly one - this forces the per-pixel evaluation to be done on the // Y axis and thus adheres to the rule of inclusive right (instead of // inclusive left) for slope == 1 cases if ( labs( iXSize ) >= labs( iYSize ) ) { // here for X major m_bLineXMajor = TRUE; m_fLineMajorLength = (FLOAT)iXSize * (1./16.); // line function: y = F(x) = ( [0]*x + [1] ) / [2] m_iLineEdgeFunc[0] = iYSize; m_iLineEdgeFunc[1] = (INT64)m_iY0*(INT64)m_iX1 - (INT64)m_iY1*(INT64)m_iX0; m_iLineEdgeFunc[2] = iXSize; } else { // here for Y major m_bLineXMajor = FALSE; m_fLineMajorLength = (FLOAT)iYSize * (1./16.); // line function: x = F(y) = ( [0]*y + [1] ) / [2] m_iLineEdgeFunc[0] = iXSize; m_iLineEdgeFunc[1] = (INT64)m_iX0*(INT64)m_iY1 - (INT64)m_iX1*(INT64)m_iY0; m_iLineEdgeFunc[2] = iYSize; } BOOL bSlopeIsOne = ( labs( iXSize ) == labs( iYSize ) ); BOOL bSlopeIsPosOne = bSlopeIsOne && ( ( (FLOAT)m_iLineEdgeFunc[0]/(FLOAT)m_iLineEdgeFunc[2] ) > 0. ); // compute candidate pixel location for line endpoints // // n n // O-------* *-------O // n-.5 n+.5 n-.5 n+.5 // // Nearest Ceiling Nearest Floor // // always nearest ceiling for Y; use nearest floor for X for exception (slope == +1) // case else use nearest ceiling // // nearest ceiling of Y is ceil( Y - .5), and is done by converting to floor via: // // ceil( A/B ) = floor( (A+B-1)/B ) // // where A is coordinate - .5, and B is 0x10 (thus A/B is an n.4 fixed point number) // // A+B-1 = ( (Y - half) + B - 1 = ( (Y-0x8) + 0x10 - 0x1 = Y + 0x7 // since B is 2**4, divide by B is right shift by 4 // INT32 iPixX0 = ( m_iX0 + ( bSlopeIsPosOne ? 0x8 : 0x7 ) ) >> 4; INT32 iPixX1 = ( m_iX1 + ( bSlopeIsPosOne ? 0x8 : 0x7 ) ) >> 4; INT32 iPixY0 = ( m_iY0 + 0x7 ) >> 4; INT32 iPixY1 = ( m_iY1 + 0x7 ) >> 4; // check for vertices in/out of diamond BOOL bV0InDiamond = PointDiamondCheck( m_iX0 - (iPixX0<<4), m_iY0 - (iPixY0<<4), bSlopeIsOne, bSlopeIsPosOne ); BOOL bV1InDiamond = PointDiamondCheck( m_iX1 - (iPixX1<<4), m_iY1 - (iPixY1<<4), bSlopeIsOne, bSlopeIsPosOne ); // compute step value m_iLineStep = ( m_fLineMajorLength > 0 ) ? ( +1 ) : ( -1 ); // compute float and integer major start (V0) and end (V1) positions INT32 iLineMajor0 = ( m_bLineXMajor ) ? ( m_iX0 ) : ( m_iY0 ); INT32 iLineMajor1 = ( m_bLineXMajor ) ? ( m_iX1 ) : ( m_iY1 ); m_iLineMin = ( m_bLineXMajor ) ? ( iPixX0 ) : ( iPixY0 ); m_iLineMax = ( m_bLineXMajor ) ? ( iPixX1 ) : ( iPixY1 ); // need to do lots of compares which are flipped if major direction is negative #define LINEDIR_CMP( _A, _B ) \ ( ( m_fLineMajorLength > 0 ) ? ( (_A) < (_B) ) : ( (_A) > (_B) ) ) // do first pixel handling - keep first pixel if not in or behind diamond if ( !( bV0InDiamond || LINEDIR_CMP( iLineMajor0, (m_iLineMin<<4) ) ) ) { m_iLineMin += m_iLineStep; } // do last-pixel handling - keep last pixel if past diamond (in which case // the pixel is always filled) or if in diamond and rendering last pixel if ( !( ( !bV1InDiamond && LINEDIR_CMP( (m_iLineMax<<4), iLineMajor1 ) ) || ( bV1InDiamond && bLastPixel ) ) ) { m_iLineMax -= m_iLineStep; } // return if no (major) extent (both before and after clamping to render buffer) if ( LINEDIR_CMP( m_iLineMax, m_iLineMin ) ) return TRUE; // snap major extent to render buffer INT16 iRendBufMajorMin = m_bLineXMajor ? pClip->left : pClip->top; INT16 iRendBufMajorMax = m_bLineXMajor ? pClip->right : pClip->bottom; if ( ( ( m_iLineMin < iRendBufMajorMin ) && ( m_iLineMax < iRendBufMajorMin ) ) || ( ( m_iLineMin > iRendBufMajorMax ) && ( m_iLineMax > iRendBufMajorMax ) ) ) { return TRUE; } m_iLineMin = MAX( 0, MIN( iRendBufMajorMax, m_iLineMin ) ); m_iLineMax = MAX( 0, MIN( iRendBufMajorMax, m_iLineMax ) ); // return if no (major) extent if ( LINEDIR_CMP( m_iLineMax, m_iLineMin ) ) return TRUE; // number of steps to iterate m_cLineSteps = abs( m_iLineMax - m_iLineMin ); // initial state for per-pixel line iterator m_iMajorCoord = m_iLineMin; // compute float versions of snapped coord data m_fX0 = (FLOAT)m_iX0 * 1.0F/16.0F; m_fY0 = (FLOAT)m_iY0 * 1.0F/16.0F; // compute linear function for 1/W (for perspective correction) m_fRHW0 = *(pVtx0+3); m_fRHW1 = *(pVtx1+3); FLOAT fDelta = ( m_fRHW1 - m_fRHW0 ) / m_fLineMajorLength; m_fRHWA = ( m_bLineXMajor ) ? ( fDelta ) : ( 0. ); m_fRHWB = ( m_bLineXMajor ) ? ( 0. ) : ( fDelta ); m_fRHWC = m_fRHW0 - ( m_fRHWA * m_fX0 ) - ( m_fRHWB * m_fY0 ); return FALSE; } //----------------------------------------------------------------------------- // // DivRoundDown(A,B) = ceiling(A/B - 1/2) // // ceiling(A/B - 1/2) == floor(A/B + 1/2 - epsilon) // == floor( (A + (B/2 - epsilon))/B ) // // Does correct thing for all sign combinations of A and B. // //----------------------------------------------------------------------------- static INT64 DivRoundDown(INT64 iA, INT32 iB) { INT32 i = 0; static const INT32 iEps[3] = { 1, // iA > 0, iB > 0 0, // iA < 0, iB > 0 OR iA > 0, iB < 0 1 // iA < 0, iB < 0 }; if (iA < 0) { i++; iA = -iA; } if (iB < 0) { i++; iB = -iB; } iA += (iB-iEps[i]) >> 1; iA /= iB; if (iEps[i] == 0) iA = -iA; return(iA); } //----------------------------------------------------------------------------- // // DoScanCnvLine - Walks the line major axis, computes the appropriate minor // axis coordinate, and generates pixels. // //----------------------------------------------------------------------------- void RefRast::StepLine( void ) { // evaluate line function to compute minor coord for this major INT64 iMinorCoord = ( ( m_iLineEdgeFunc[0] * (INT64)(m_iMajorCoord<<4) ) + m_iLineEdgeFunc[1] ); iMinorCoord = DivRoundDown(iMinorCoord, m_iLineEdgeFunc[2]<<4); // grab x,y m_iX[0] = m_bLineXMajor ? m_iMajorCoord : iMinorCoord; m_iY[0] = m_bLineXMajor ? iMinorCoord : m_iMajorCoord; // step major for next evaluation m_iMajorCoord += m_iLineStep; } /////////////////////////////////////////////////////////////////////////////// // // Multi-Sample Controls // /////////////////////////////////////////////////////////////////////////////// #define _SetSampleDelta( _SampleNumber, _XOffset, _YOffset ) \ { \ m_SampleDelta[_SampleNumber][0] = ((INT32)((_XOffset)*16.F)); \ m_SampleDelta[_SampleNumber][1] = ((INT32)((_YOffset)*16.F)); \ } void RefRast::SetSampleMode( UINT MultiSamples, BOOL bAntialias ) { switch (MultiSamples) { default: case 1: m_SampleCount = 1; _SetSampleDelta( 0, 0., 0. ); break; case 4: m_SampleCount = 4; _SetSampleDelta( 0, -.25, -.25 ); _SetSampleDelta( 1, +.25, -.25 ); _SetSampleDelta( 2, +.25, +.25 ); _SetSampleDelta( 3, -.25, +.25 ); break; case 9: m_SampleCount = 9; _SetSampleDelta( 0, -.333, -.333 ); _SetSampleDelta( 1, -.333, 0.0 ); _SetSampleDelta( 2, -.333, +.333 ); _SetSampleDelta( 3, 0.0, -.333 ); _SetSampleDelta( 4, 0.0, 0.0 ); _SetSampleDelta( 5, 0.0, +.333 ); _SetSampleDelta( 6, +.333, -.333 ); _SetSampleDelta( 7, +.333, 0.0 ); _SetSampleDelta( 8, +.333, +.333 ); break; } // if not FSAA then sample all at pixel center if (!bAntialias) { for (UINT Sample=0; Sample