//---------------------------------------------------------------------------- // // tri.cpp // // PrimProcessor top-level triangle methods. // // Copyright (C) Microsoft Corporation, 1997. // //---------------------------------------------------------------------------- #include "rgb_pch.h" #pragma hdrstop #include "d3dutil.h" #include "setup.hpp" #include "attrs_mh.h" #include "tstp_mh.h" #include "walk_mh.h" #include "rsdbg.hpp" DBG_DECLARE_FILE(); // Disallow fixed-point edge walkers to be chosen or not. #if 0 #define DISALLOW_FIXED #endif // Maximum length of a PWL span. Short to make piecewise-linear // approximation more accurate. #define MAX_PWL_SPAN_LEN 16 // Maximum normal span length. #define MAX_SPAN_LEN 256 //---------------------------------------------------------------------------- // // PrimProcessor::SetTriFunctions // // Set up function pointers for triangle processing. // //---------------------------------------------------------------------------- inline void PrimProcessor::SetTriFunctions(void) { #if DBG if ((RSGETFLAGS(DBG_USER_FLAGS) & RSU_FORCE_PIXEL_SPANS) == 0) #else if ((m_StpCtx.uFlags & TRIF_RASTPRIM_OVERFLOW) == 0) #endif { // Valid deltas. If mipmapping or global fog is on then // only allow short subspans so that they can be done // reasonably accurately via piecewise linear interpolation. #ifdef PWL_FOG if (m_StpCtx.uFlags & (PRIMSF_LOD_USED | PRIMSF_GLOBAL_FOG_USED)) #else if (m_StpCtx.uFlags & PRIMSF_LOD_USED) #endif { m_StpCtx.cMaxSpan = MAX_PWL_SPAN_LEN; } else { // No mipmapping so we can handle much larger spans. // Color values only have 8 bits of fraction so // we still need to worry about error accumulation. // Cut long spans to cap accumulated error. m_StpCtx.cMaxSpan = MAX_SPAN_LEN; } } else { // Invalid deltas. There's no way to communicate deltas to // the span routines so chop spans into pixels. // This case can only occur with very narrow triangles so // this isn't as expensive as it might seem at first. m_StpCtx.cMaxSpan = 1; } BOOL bFixed = FALSE; #ifdef STEP_FIXED // No ramp support. RSASSERT(m_StpCtx.pCtx->BeadSet != D3DIBS_RAMP); #endif if ((m_StpCtx.uFlags & PRIMF_TRIVIAL_ACCEPT_X) && #if DBG (RSGETFLAGS(DBG_USER_FLAGS) & RSU_FORCE_GENERAL_WALK) == 0 && #endif m_iXWidth <= m_StpCtx.cMaxSpan) { if ((m_StpCtx.uFlags & PRIMSF_SLOW_USED) != PRIMSF_Z_USED) { // If any slow attrs are on or Z is off use the general // function. m_StpCtx.pfnWalkTrapSpans = WalkTrapEitherSpans_Any_NoClip; } #if defined(STEP_FIXED) && !defined(DISALLOW_FIXED) // Attribute conversion can be a dominant cost for // triangles with very few spans, so avoid using fixed point // edge walking for them. else if ((m_StpCtx.uFlags & PRIMF_FIXED_OVERFLOW) == 0 && m_uHeight20 > 3) { m_StpCtx.pfnWalkTrapSpans = g_pfnWalkTrapFixedSpansNoClipTable[m_iAttrFnIdx]; bFixed = TRUE; } #endif else if (m_StpCtx.pCtx->BeadSet == D3DIBS_RAMP) { m_StpCtx.pfnWalkTrapSpans = g_pfnRampWalkTrapFloatSpansNoClipTable[m_iAttrFnIdx]; } else { m_StpCtx.pfnWalkTrapSpans = g_pfnWalkTrapFloatSpansNoClipTable[m_iAttrFnIdx]; } } else { // No special cases, just a general function. m_StpCtx.pfnWalkTrapSpans = WalkTrapEitherSpans_Any_Clip; } #ifdef STEP_FIXED if (bFixed) { RSASSERT((m_StpCtx.uFlags & PRIMSF_SLOW_USED) == PRIMSF_Z_USED); m_StpCtx.pfnAddAttrs = g_pfnAddFixedAttrsTable[m_iAttrFnIdx]; m_StpCtx.pfnFillSpanAttrs = g_pfnFillSpanFixedAttrsTable[m_iAttrFnIdx]; PFN_FLOATATTRSTOFIXED pfnFloatAttrsToFixed; pfnFloatAttrsToFixed = g_pfnFloatAttrsToFixedTable[m_iAttrFnIdx]; pfnFloatAttrsToFixed(&m_StpCtx.Attr, &m_StpCtx.Attr); pfnFloatAttrsToFixed(&m_StpCtx.DAttrNC, &m_StpCtx.DAttrNC); pfnFloatAttrsToFixed(&m_StpCtx.DAttrCY, &m_StpCtx.DAttrCY); } else { if ((m_StpCtx.uFlags & PRIMSF_SLOW_USED) != PRIMSF_Z_USED) { // If any slow attrs are on or Z is off use the general functions. m_StpCtx.pfnAddAttrs = AddFloatAttrs_Any; m_StpCtx.pfnFillSpanAttrs = FillSpanFloatAttrs_Any_Either; } else { m_StpCtx.pfnAddAttrs = g_pfnAddFloatAttrsTable[m_iAttrFnIdx]; m_StpCtx.pfnFillSpanAttrs = g_pfnFillSpanFloatAttrsTable[m_iAttrFnIdx]; } } // Scaled attr functions already set since they only depend on // m_iAttrFnIdx. #else // STEP_FIXED // All attr functions already set since they only depend on // m_iAttrFnIdx. #endif // STEP_FIXED } //---------------------------------------------------------------------------- // // PrimProcessor::Tri // // Calls triangle setup. If a triangle is produced by setup // this routine walks edges, generating spans into the buffer. // //---------------------------------------------------------------------------- HRESULT PrimProcessor::Tri(LPD3DTLVERTEX pV0, LPD3DTLVERTEX pV1, LPD3DTLVERTEX pV2) { HRESULT hr; hr = DD_OK; #if DBG hr = ValidateVertex(pV0); if (hr != DD_OK) { RSDPF(("PrimProcessor::Tri, Invalid V0\n")); return hr; } hr = ValidateVertex(pV1); if (hr != DD_OK) { RSDPF(("PrimProcessor::Tri, Invalid V1\n")); return hr; } hr = ValidateVertex(pV2); if (hr != DD_OK) { RSDPF(("PrimProcessor::Tri, Invalid V2\n")); return hr; } #endif // Clear per-triangle flags. m_StpCtx.uFlags &= ~(PRIMF_ALL | TRIF_ALL); RSDPFM((RSM_FLAGS, "m_uPpFlags: 0x%08X, m_StpCtx.uFlags: 0x%08X\n", m_uPpFlags, m_StpCtx.uFlags)); RSDPFM((RSM_TRIS, "Tri\n")); RSDPFM((RSM_TRIS, " V0 (%f,%f,%f)\n", pV0->dvSX, pV0->dvSY, pV0->dvSZ)); RSDPFM((RSM_TRIS, " V1 (%f,%f,%f)\n", pV1->dvSX, pV1->dvSY, pV1->dvSZ)); RSDPFM((RSM_TRIS, " V2 (%f,%f,%f)\n", pV2->dvSX, pV2->dvSY, pV2->dvSZ)); GET_PRIM(); // Set up the triangle and see if anything was produced. // Triangles may not be produced due to: // Face culling. // Trivial rejection against the clip rect. // Zero pixel coverage. if (TriSetup(pV0, pV1, pV2)) { // Compute initial buffer pointers for the scanline. m_StpCtx.Attr.pSurface = m_StpCtx.pCtx->pSurfaceBits + m_StpCtx.iX * m_StpCtx.pCtx->iSurfaceStep + m_StpCtx.iY * m_StpCtx.pCtx->iSurfaceStride; if (m_StpCtx.uFlags & PRIMSF_Z_USED) { m_StpCtx.Attr.pZ = m_StpCtx.pCtx->pZBits + m_StpCtx.iX * m_StpCtx.pCtx->iZStep + m_StpCtx.iY * m_StpCtx.pCtx->iZStride; } // uSpans and pNext have already been initialized. SetTriFunctions(); COMMIT_PRIM(FALSE); if (m_uHeight10 > 0) { hr = m_StpCtx.pfnWalkTrapSpans(m_uHeight10, &m_StpCtx.X10, &m_StpCtx, m_uHeight21 > 0); if (hr != DD_OK) { return hr; } } if (m_uHeight21 > 0) { hr = m_StpCtx.pfnWalkTrapSpans(m_uHeight21, &m_StpCtx.X21, &m_StpCtx, FALSE); if (hr != DD_OK) { return hr; } } #if DBG if (RSGETFLAGS(DBG_USER_FLAGS) & RSU_FLUSH_AFTER_PRIM) { Flush(); } #endif } return hr; } #if DBG //---------------------------------------------------------------------------- // // PrimProcessor::ValidateVertex // // Checks the ranges of verifiable contents of vertex, to avoid setting up // garbage. // //---------------------------------------------------------------------------- inline HRESULT PrimProcessor::ValidateVertex(LPD3DTLVERTEX pV) { // from the OptSwExtCaps.dvGuardBand caps. if ((pV->sx < -32768.f) || (pV->sx > 32767.f) || (pV->sy < -32768.f) || (pV->sy > 32767.f)) { RSDPF(("ValidateVertex: x,y out of guardband range (%f,%f)\n",pV->sx,pV->sy)); return DDERR_INVALIDPARAMS; } if (m_StpCtx.pCtx->pdwRenderState[D3DRS_ZENABLE] || m_StpCtx.pCtx->pdwRenderState[D3DRS_ZWRITEENABLE]) { // Allow a little slack for those generating triangles exactly on the // depth limit. Needed for Quake. if ((pV->sz < -0.00015f) || (pV->sz > 1.00015f)) { RSDPF(("ValidateVertex: z out of range (%f)\n",pV->sz)); return DDERR_INVALIDPARAMS; } } if (m_StpCtx.pCtx->cActTex > 0) { if (true ||m_StpCtx.pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE]) { if (pV->rhw <= 0 ) { RSDPF(("ValidateVertex: rhw out of range (%f)\n",pV->rhw)); return DDERR_INVALIDPARAMS; } } // from OptSwExtCaps.dwMaxTextureRepeat cap. if ((pV->tu > 256.0F) || (pV->tu < -256.0F) || (pV->tv > 256.0F) || (pV->tv < -256.0F)) { RSDPF(("ValidateVertex: tu,tv out of range (%f,%f)\n",pV->tu,pV->tv)); return DDERR_INVALIDPARAMS; } if (m_StpCtx.pCtx->cActTex > 1) { PRAST_GENERIC_VERTEX pGV = (PRAST_GENERIC_VERTEX)pV; if ((pGV->texCoord[1].tu > 256.0F) || (pGV->texCoord[1].tu < -256.0F) || (pGV->texCoord[1].tv > 256.0F) || (pGV->texCoord[1].tv < -256.0F)) { RSDPF(("ValidateVertex: texCoord[1].tu,texCoord[1].tv out of range (%f,%f)\n",pGV->texCoord[1].tu,pGV->texCoord[1].tv)); return DDERR_INVALIDPARAMS; } } } return DD_OK; } #endif