//---------------------------------------------------------------------------- // // buffer.cpp // // PrimProcessor buffering 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(); // Define to use new/delete instead of VirtualAlloc/VirtualFree. #if 0 #define USE_CPP_HEAP #endif // Define to show FP exceptions. #if 0 #define UNMASK_EXCEPTIONS #endif //---------------------------------------------------------------------------- // // PrimProcessor::PrimProcessor // // Initializes a triangle processor to an invalid state. // //---------------------------------------------------------------------------- PrimProcessor::PrimProcessor(void) { // Zero everything to NULL initial pointers and eliminate FP garbage. memset(this, 0, sizeof(PrimProcessor)); // TODO: Fix this unextendable stuff. m_StpCtx.PrimProcessor = (PVOID)this; // Initialize to values that will force a validation. // ATTENTION - Default to normalizing RHW. This is a performance hit // and should be removed if possible. m_uPpFlags = PPF_STATE_CHANGED | PPF_NORMALIZE_RHW; m_PrimType = D3DPT_FORCE_DWORD; m_VertType = RAST_FORCE_DWORD; } //---------------------------------------------------------------------------- // // PrimProcessor::Initialize // // Initializes the triangle processor to an active state. // //---------------------------------------------------------------------------- #define CACHE_LINE 32 #define BUFFER_SIZE 4096 // Uncomment to force a flush every span for debug purposes //#define BUFFER_SIZE ((8 * sizeof(D3DI_RASTSPAN)) + sizeof(D3DI_RASTPRIM)) // TODO: Move into constructor? How many places called? HRESULT PrimProcessor::Initialize(void) { HRESULT hr; INT32 uSize = sizeof(D3DI_RASTPRIM); // Assert that both RASTPRIM and RASTSPAN are multiples of the cache // line size so that everything in the buffer stays cache aligned. RSASSERT((uSize & (CACHE_LINE - 1)) == 0 && (uSize & (CACHE_LINE - 1)) == 0); #ifdef USE_CPP_HEAP m_pBuffer = new UINT8[BUFFER_SIZE]; #else // Get a page-aligned buffer. m_pBuffer = (PUINT8) VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); #endif if (m_pBuffer == NULL) { return RSHRCHK(E_OUTOFMEMORY); } m_pBufferEnd = m_pBuffer+BUFFER_SIZE; #ifdef USE_CPP_HEAP // Compute cache-line aligned start in the buffer. Formulated // somewhat oddly to avoid casting a complete pointer to a DWORD and // back. m_pBufferStart = m_pBuffer + ((CACHE_LINE - ((UINT)m_pBuffer & (CACHE_LINE - 1))) & (CACHE_LINE - 1)); #else // Page aligned memory should be cache aligned. RSASSERT(((UINT_PTR)m_pBuffer & (CACHE_LINE - 1)) == 0); m_pBufferStart = m_pBuffer; #endif m_pCur = m_pBufferStart; return S_OK; } //---------------------------------------------------------------------------- // // PrimProcessor::~PrimProcessor // //---------------------------------------------------------------------------- PrimProcessor::~PrimProcessor(void) { #ifdef USE_CPP_HEAP delete m_pBuffer; #else if (m_pBuffer != NULL) { VirtualFree(m_pBuffer, 0, MEM_RELEASE); } #endif } //---------------------------------------------------------------------------- // // PrimProcessor::ResetBuffer // // Initialize buffer pointers to an empty state. // //---------------------------------------------------------------------------- inline void PrimProcessor::ResetBuffer(void) { m_pCur = m_pBufferStart; m_StpCtx.pPrim = NULL; m_pOldPrim = NULL; } //---------------------------------------------------------------------------- // // DumpPrims // // Debugging function to dump primitives sent to the span renderer. // //---------------------------------------------------------------------------- #if DBG void DumpPrims(PSETUPCTX pStpCtx) { PD3DI_RASTPRIM pPrim; UINT uOldFlags; uOldFlags = RSGETFLAGS(DBG_OUTPUT_FLAGS); RSSETFLAGS(DBG_OUTPUT_FLAGS, uOldFlags | DBG_OUTPUT_ALL_MATCH); for (pPrim = pStpCtx->pCtx->pPrim; pPrim != NULL; pPrim = pPrim->pNext) { RSDPFM((RSM_BUFPRIM, "Prim at %p, %d spans at %p\n", pPrim, pPrim->uSpans, pPrim+1)); RSDPFM((RSM_BUFPRIM | RSM_OOW, " DOoWDX %X (%f)\n", pPrim->iDOoWDX, (FLOAT)pPrim->iDOoWDX / OOW_SCALE)); if ((RSGETFLAGS(DBG_OUTPUT_MASK) & RSM_BUFSPAN) || (RSGETFLAGS(DBG_USER_FLAGS) & (RSU_MARK_SPAN_EDGES | RSU_CHECK_SPAN_EDGES))) { PD3DI_RASTSPAN pSpan; UINT16 i; pSpan = (PD3DI_RASTSPAN)(pPrim+1); for (i = 0; i < pPrim->uSpans; i++) { RSDPFM((RSM_BUFSPAN, " Span at (%d,%d), pix %c%d, S %p Z %p\n", pSpan->uX, pSpan->uY, (pPrim->uFlags & D3DI_RASTPRIM_X_DEC) ? '-' : '+', pSpan->uPix, pSpan->pSurface, pSpan->pZ)); if (RSGETFLAGS(DBG_USER_FLAGS) & (RSU_MARK_SPAN_EDGES | RSU_CHECK_SPAN_EDGES)) { PUINT16 pPix; pPix = (PUINT16)pSpan->pSurface; if (RSGETFLAGS(DBG_USER_FLAGS) & RSU_CHECK_SPAN_EDGES) { if (*pPix != 0) { RSDPF((" Overwrite at %p: %X\n", pPix, *pPix)); } } if (RSGETFLAGS(DBG_USER_FLAGS) & RSU_MARK_SPAN_EDGES) { *pPix = 0xffff; } if (pSpan->uPix > 1) { if (pPrim->uFlags & D3DI_RASTPRIM_X_DEC) { pPix = (PUINT16)pSpan->pSurface - (pSpan->uPix - 1); } else { pPix = (PUINT16)pSpan->pSurface + (pSpan->uPix - 1); } if (RSGETFLAGS(DBG_USER_FLAGS) & RSU_CHECK_SPAN_EDGES) { if (*pPix != 0) { RSDPF((" Overwrite at %p: %X\n", pPix, *pPix)); } } if (RSGETFLAGS(DBG_USER_FLAGS) & RSU_MARK_SPAN_EDGES) { *pPix = 0xffff; } } } FLOAT fZScale; if (pStpCtx->pCtx->iZBitCount == 16) { fZScale = Z16_SCALE; } else { fZScale = Z32_SCALE; } RSDPFM((RSM_BUFSPAN | RSM_Z, " Z %X (%f)\n", pSpan->uZ, (FLOAT)pSpan->uZ / fZScale)); RSDPFM((RSM_BUFSPAN | RSM_DIFF, " D %X,%X,%X,%X (%f,%f,%f,%f)\n", pSpan->uB, pSpan->uG, pSpan->uR, pSpan->uA, (FLOAT)pSpan->uB / COLOR_SCALE, (FLOAT)pSpan->uG / COLOR_SCALE, (FLOAT)pSpan->uR / COLOR_SCALE, (FLOAT)pSpan->uA / COLOR_SCALE)); RSDPFM((RSM_BUFSPAN | RSM_SPEC, " S %X,%X,%X (%f,%f,%f)\n", pSpan->uBS, pSpan->uGS, pSpan->uRS, (FLOAT)pSpan->uBS / COLOR_SCALE, (FLOAT)pSpan->uGS / COLOR_SCALE, (FLOAT)pSpan->uRS / COLOR_SCALE)); RSDPFM((RSM_BUFSPAN | RSM_DIDX, " I %X,%X (%f,%f)\n", pSpan->iIdx, pSpan->iIdxA, (FLOAT)pSpan->iIdx / INDEX_COLOR_SCALE, (FLOAT)pSpan->iIdxA / INDEX_COLOR_SCALE)); RSDPFM((RSM_BUFSPAN | RSM_OOW, " OoW %X (%f), W %X (%f)\n", pSpan->iOoW, (FLOAT)pSpan->iOoW / OOW_SCALE, pSpan->iW, (FLOAT)pSpan->iW / W_SCALE)); RSDPFM((RSM_BUFSPAN | RSM_LOD, " LOD %X (%f), DLOD %X (%f)\n", pSpan->iLOD, (FLOAT)pSpan->iLOD / LOD_SCALE, pSpan->iDLOD, (FLOAT)pSpan->iDLOD / LOD_SCALE)); if (pStpCtx->uFlags & PRIMSF_PERSP_USED) { RSDPFM((RSM_BUFSPAN | RSM_TEX1, " PTex1 %X,%X (%f,%f) (%f,%f)\n", pSpan->UVoW[0].iUoW, pSpan->UVoW[0].iVoW, (FLOAT)pSpan->UVoW[0].iUoW / TEX_SCALE, (FLOAT)pSpan->UVoW[0].iVoW / TEX_SCALE, ((FLOAT)pSpan->UVoW[0].iUoW * OOW_SCALE) / (TEX_SCALE * (FLOAT)pSpan->iOoW), ((FLOAT)pSpan->UVoW[0].iVoW * OOW_SCALE) / (TEX_SCALE * (FLOAT)pSpan->iOoW))); } else { RSDPFM((RSM_BUFSPAN | RSM_TEX1, " ATex1 %X,%X (%f,%f)\n", pSpan->UVoW[0].iUoW, pSpan->UVoW[0].iVoW, (FLOAT)pSpan->UVoW[0].iUoW / TEX_SCALE, (FLOAT)pSpan->UVoW[0].iVoW / TEX_SCALE)); } RSDPFM((RSM_BUFSPAN | RSM_FOG, " Fog %X (%f), DFog %X (%f)\n", pSpan->uFog, (FLOAT)pSpan->uFog / FOG_SCALE, pSpan->iDFog, (FLOAT)pSpan->iDFog / FOG_SCALE)); pSpan++; } } } RSSETFLAGS(DBG_OUTPUT_FLAGS, uOldFlags); } #endif // DBG //---------------------------------------------------------------------------- // // PrimProcessor::Flush // // Flushes any remaining data from the buffer. // //---------------------------------------------------------------------------- HRESULT PrimProcessor::Flush(void) { HRESULT hr; if (m_pCur - m_pBufferStart > sizeof(D3DI_RASTPRIM)) { // Process data. m_StpCtx.pCtx->pPrim = (PD3DI_RASTPRIM)m_pBufferStart; m_StpCtx.pCtx->pNext = NULL; #if DBG if ((RSGETFLAGS(DBG_OUTPUT_MASK) & (RSM_BUFPRIM | RSM_BUFSPAN)) || (RSGETFLAGS(DBG_USER_FLAGS) & (RSU_MARK_SPAN_EDGES | RSU_CHECK_SPAN_EDGES))) { DumpPrims(&m_StpCtx); } if ((RSGETFLAGS(DBG_USER_FLAGS) & RSU_NO_RENDER_SPANS) == 0) { if (RSGETFLAGS(DBG_USER_FLAGS) & RSU_BREAK_ON_RENDER_SPANS) { DebugBreak(); } RSHRCHK(m_StpCtx.pCtx->pfnRenderSpans(m_StpCtx.pCtx)); } else { hr = DD_OK; } #else hr = m_StpCtx.pCtx->pfnRenderSpans(m_StpCtx.pCtx); #endif ResetBuffer(); } else { hr = DD_OK; } return hr; } //---------------------------------------------------------------------------- // // PrimProcessor::FlushPartial // // Flushes the buffer in the middle of a primitive. Preserves last // partial primitive and replaces it in the buffer after the flush. // //---------------------------------------------------------------------------- HRESULT PrimProcessor::FlushPartial(void) { D3DI_RASTPRIM SavedPrim; HRESULT hr; RSDPFM((RSM_BUFFER, "FlushPartial, saving prim at %p, Y %d\n", m_StpCtx.pPrim, m_StpCtx.iY)); // Not enough space. Flush current buffer. We need to // save the current prim and put it back in the buffer after the // flush since it's being extended. SavedPrim = *m_StpCtx.pPrim; RSHRRET(Flush()); GET_PRIM(); *m_StpCtx.pPrim = SavedPrim; COMMIT_PRIM(FALSE); return DD_OK; } //---------------------------------------------------------------------------- // // PrimProcessor::AppendPrim // // Ensures that some primitive is active in the buffer for spans to // be added to. If no valid primitive is available to append to, // a zeroed primitive is committed into the buffer. // //---------------------------------------------------------------------------- HRESULT PrimProcessor::AppendPrim(void) { // If there's no primitive or the current primitive has not // been committed, commit a clean primitive into the buffer. if (m_StpCtx.pPrim == NULL || (PUINT8)m_StpCtx.pPrim == m_pCur) { GET_PRIM(); COMMIT_PRIM(TRUE); } return DD_OK; } //---------------------------------------------------------------------------- // // PrimProcessor::Begin // // Resets the buffer to an empty state in preparation for incoming // triangles. // //---------------------------------------------------------------------------- void PrimProcessor::Begin(void) { UINT16 uFpCtrl; FPU_GET_MODE(uFpCtrl); m_uFpCtrl = uFpCtrl; uFpCtrl = FPU_MODE_CHOP_ROUND( FPU_MODE_LOW_PRECISION( FPU_MODE_MASK_EXCEPTIONS(m_uFpCtrl))); #if defined(_X86_) && defined(UNMASK_EXCEPTIONS) // Unmask some exceptions so that we can eliminate them. // This requires a safe set to clear any exceptions that // are currently asserted. // // Exceptions left masked: // Precision, denormal. // Exceptions unmasked: // Underflow, overflow, divzero, invalid op. uFpCtrl &= ~0x1d; FPU_SAFE_SET_MODE(uFpCtrl); #else FPU_SET_MODE(uFpCtrl); #endif m_uPpFlags |= PPF_IN_BEGIN; ResetBuffer(); } //---------------------------------------------------------------------------- // // PrimProcessor::End // // Flushes if necessary and cleans up. // //---------------------------------------------------------------------------- HRESULT PrimProcessor::End(void) { HRESULT hr; if (m_pCur - m_pBufferStart > sizeof(D3DI_RASTPRIM)) { RSHRCHK(Flush()); } else { hr = DD_OK; } UINT16 uFpCtrl = m_uFpCtrl; FPU_SAFE_SET_MODE(uFpCtrl); m_uPpFlags &= ~PPF_IN_BEGIN; return hr; } //---------------------------------------------------------------------------- // // PrimProcessor::SetCtx // // Sets the rasterization context to operate in. // //---------------------------------------------------------------------------- void PrimProcessor::SetCtx(PD3DI_RASTCTX pCtx) { // This function can't be called inside a Begin/End pair. This // is enforced so that we don't have to worry about the span // rendering function changing in the middle of a batch. RSASSERT((m_uPpFlags & PPF_IN_BEGIN) == 0); m_StpCtx.pCtx = pCtx; } //---------------------------------------------------------------------------- // // PrimProcessor::AllocSpans // // Checks to see if there's room in the buffer for the requested number // of spans. If so the buffer pointer is updated and a pointer is returned. // If the requested number is not available but some reasonable number is, // return that many. Otherwise the buffer is flushed and the process starts // over. The "reasonable" number must therefore be no more than what // can fit in the buffer at once. // //---------------------------------------------------------------------------- // Space for enough spans to avoid a flush. #define AVOID_FLUSH_SPACE (8 * sizeof(D3DI_RASTSPAN)) HRESULT PrimProcessor::AllocSpans(PUINT pcSpans, PD3DI_RASTSPAN *ppSpan) { PD3DI_RASTSPAN pSpan; HRESULT hr; UINT uSpanSize; RSASSERT(AVOID_FLUSH_SPACE <= (BUFFER_SIZE - sizeof(D3DI_RASTPRIM))); // The multiplies and divides here will be really bad unless // RASTPRIM is a nice power-of-two in size. RSASSERT((sizeof(D3DI_RASTSPAN) & (sizeof(D3DI_RASTSPAN) - 1)) == 0); uSpanSize = *pcSpans * sizeof(D3DI_RASTSPAN); for (;;) { // First check for space for all requested spans. if (m_pCur + uSpanSize > m_pBufferEnd) { // Not enough space for everything, so see if we have // enough space to avoid a flush. if (m_pCur + AVOID_FLUSH_SPACE > m_pBufferEnd) { // Not enough space, so flush. RSHRCHK(FlushPartial()); if (hr != DD_OK) { *pcSpans = 0; return hr; } // Loop around. Flush is guaranteed to at least produce // AVOID_FLUSH_SPACE so the loop will always exit. } else { // Not enough space for everything but enough space // to return some. Set new span count. *pcSpans = (UINT)((m_pBufferEnd - m_pCur) / sizeof(D3DI_RASTSPAN)); uSpanSize = *pcSpans * sizeof(D3DI_RASTSPAN); break; } } else { break; } } pSpan = (PD3DI_RASTSPAN)m_pCur; m_pCur += uSpanSize; *ppSpan = pSpan; RSDPFM((RSM_BUFFER, "Alloc %d spans at %p, cur %p\n", *pcSpans, pSpan, m_pCur)); return DD_OK; } //---------------------------------------------------------------------------- // // PrimProcessor::FreeSpans and FreeSpans // // Returns space given out by AllocSpans. // //---------------------------------------------------------------------------- void PrimProcessor::FreeSpans(UINT cSpans) { m_pCur -= cSpans * sizeof(D3DI_RASTSPAN); RSDPFM((RSM_BUFFER, "Free %d spans at %p, cur %p\n", cSpans, m_pCur + cSpans * sizeof(D3DI_RASTSPAN), m_pCur)); }