Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

604 lines
18 KiB

//----------------------------------------------------------------------------
//
// 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));
}