windows-nt/Source/XPSP1/NT/multimedia/directx/dxg/d3d8/fe/vshader.cpp

2611 lines
100 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*============================================================================
*
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
*
* File: vshader.cpp
* Content: SetStreamSource and VertexShader
* software implementation.
*
****************************************************************************/
#include "pch.cpp"
#pragma hdrstop
#include "ibuffer.hpp"
#include "fe.h"
#include "ddibase.h"
#include "pvvid.h"
void __Transpose(D3DMATRIXI* m, D3DMATRIX* res)
{
res->_11 = m->_11;
res->_12 = m->_21;
res->_13 = m->_31;
res->_14 = m->_41;
res->_21 = m->_12;
res->_22 = m->_22;
res->_23 = m->_32;
res->_24 = m->_42;
res->_31 = m->_13;
res->_32 = m->_23;
res->_33 = m->_33;
res->_34 = m->_43;
res->_41 = m->_14;
res->_42 = m->_24;
res->_43 = m->_34;
res->_44 = m->_44;
}
//-----------------------------------------------------------------------------
// Forward definitions
//
void CD3DHal_DrawPrimitive(CD3DBase* pBaseDevice, D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex, UINT PrimitiveCount);
void CD3DHal_DrawIndexedPrimitive(CD3DBase* pBaseDevice,
D3DPRIMITIVETYPE PrimitiveType,
UINT BaseIndex,
UINT MinIndex, UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount);
void CD3DHal_DrawNPatch(CD3DBase* pBaseDevice, D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex, UINT PrimitiveCount);
void CD3DHal_DrawIndexedNPatch(CD3DBase* pBaseDevice,
D3DPRIMITIVETYPE PrimitiveType,
UINT BaseIndex,
UINT MinIndex, UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount);
//-----------------------------------------------------------------------------
void __declspec(nothrow) CD3DHal::PickDrawPrimFn()
{
if (!(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING))
{
m_pfnDrawPrim = m_pDDI->GetDrawPrimFunction();
m_pfnDrawIndexedPrim = m_pDDI->GetDrawIndexedPrimFunction();
if (m_dwRuntimeFlags & D3DRT_DONPATCHCONVERSION)
{
m_pfnDrawPrimFromNPatch = m_pfnDrawPrim;
m_pfnDrawIndexedPrimFromNPatch = m_pfnDrawIndexedPrim;
m_pfnDrawPrim = CD3DHal_DrawNPatch;
m_pfnDrawIndexedPrim = CD3DHal_DrawIndexedNPatch;
}
}
else
{
DWORD dwDeviceFlags = m_pv->dwDeviceFlags;
BOOL bCallDriver;
if (Enum()->GetAppSdkVersion() == D3D_SDK_VERSION_DX8)
{
bCallDriver = dwDeviceFlags & D3DDEV_TRANSFORMEDFVF &&
(dwDeviceFlags & D3DDEV_DONOTCLIP ||
!(dwDeviceFlags & D3DDEV_VBPROCVER));
}
else
{
bCallDriver = dwDeviceFlags & D3DDEV_TRANSFORMEDFVF &&
dwDeviceFlags & D3DDEV_DONOTCLIP;
}
if (bCallDriver)
{
m_pfnDrawPrim = m_pDDI->GetDrawPrimFunction();
m_pfnDrawIndexedPrim = m_pDDI->GetDrawIndexedPrimFunction();
}
else
{
m_pfnDrawPrim = CD3DHal_DrawPrimitive;
m_pfnDrawIndexedPrim = CD3DHal_DrawIndexedPrimitive;
}
}
}
//-----------------------------------------------------------------------------
// Checks if we can call driver directly to draw the current primitive
//
inline BOOL CanCallDriver(CD3DHal* pDev, D3DPRIMITIVETYPE PrimType)
{
DWORD dwDeviceFlags = pDev->m_pv->dwDeviceFlags;
if (PrimType != D3DPT_POINTLIST)
return dwDeviceFlags & D3DDEV_TRANSFORMEDFVF &&
(dwDeviceFlags & D3DDEV_DONOTCLIP ||
pDev->Enum()->GetAppSdkVersion() == D3D_SDK_VERSION_DX8);
else
// This function could be called from DrawPointsI, which could be
// called from other Draw() function than DrawPrimitiveUP, so we need
// to check for D3DDEV_VBPROCVER. We cannot pass vertices, which are
// result of ProcessVertices(), to the driver directly
return dwDeviceFlags & D3DDEV_TRANSFORMEDFVF &&
!(pDev->m_dwRuntimeFlags & D3DRT_DOPOINTSPRITEEMULATION) &&
(dwDeviceFlags & D3DDEV_DONOTCLIP ||
(pDev->Enum()->GetAppSdkVersion() == D3D_SDK_VERSION_DX8 &&
!(dwDeviceFlags & D3DDEV_VBPROCVER)));
}
//-----------------------------------------------------------------------------
// API calls
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetStreamSourceI"
void
CD3DHal::SetStreamSourceI(CVStream* pStream)
{
if (m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING)
{
CVertexBuffer * pVB = pStream->m_pVB;
m_pv->dwDeviceFlags &= ~D3DDEV_VBPROCVER;
DWORD dwFVF = pVB->GetFVF();
if (pVB->GetClipCodes() != NULL)
{
// This vertex buffer is the output of ProcessVertices
DXGASSERT(FVF_TRANSFORMED(dwFVF));
m_pv->dwDeviceFlags |= D3DDEV_VBPROCVER;
}
if (D3DVSD_ISLEGACY(m_dwCurrentShaderHandle))
{
SetupStrides(m_pv, m_pStream[0].m_dwStride);
}
}
PickDrawPrimFn();
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetIndicesI"
void
CD3DHal::SetIndicesI(CVIndexStream* pStream)
{
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::CreateVertexShaderI"
void
CD3DHal::CreateVertexShaderI(CONST DWORD* pdwDeclaration, DWORD dwDeclSize,
CONST DWORD* pdwFunction, DWORD dwCodeSize,
DWORD dwHandle)
{
BOOL bIsCheckedBuild =
#if DBG
TRUE;
#else
FALSE;
#endif
CVShader* pShader = (CVShader*)m_pVShaderArray->GetObject(dwHandle);
if (pShader->m_dwFlags & CVShader::SOFTWARE)
{
// Build the array of all vertex elements used in the shader by going
// through all streams and elements inside each stream.
CVDeclaration* pDecl = &pShader->m_Declaration;
CVStreamDecl* pStream = pShader->m_Declaration.m_pActiveStreams;
// This is the array we build
CVElement* pVerElem = pShader->m_Declaration.m_VertexElements;
pDecl->m_dwNumElements = 0;
while (pStream)
{
for (DWORD i=0; i < pStream->m_dwNumElements; i++)
{
if (pDecl->m_dwNumElements >= __NUMELEMENTS)
{
D3D_THROW_FAIL("Declaration is using too many elements");
}
*pVerElem = pStream->m_Elements[i];
pVerElem->m_dwStreamIndex = pStream->m_dwStreamIndex;
pVerElem++;
pDecl->m_dwNumElements++;
}
pStream = (CVStreamDecl*)pStream->m_pNext;
}
if (pdwFunction != NULL)
{
// compute adjusted function pointer depending on FREE/CHECKED and PSGP
LPDWORD pdwFunctionAdj = pShader->m_pStrippedFuncCode;
if ( bIsCheckedBuild &&
((LPVOID)m_pv->pGeometryFuncs == (LPVOID)GeometryFuncsGuaranteed) ) // !PSGP
{
pdwFunctionAdj = pShader->m_pOrgFuncCode;
}
// Microsoft shader is always created.
// It is used for validation and to compute the output FVF in case
// when PSGP is present
HRESULT hr;
hr = GeometryFuncsGuaranteed->CreateShader(
pDecl->m_VertexElements,
pDecl->m_dwNumElements,
pdwFunctionAdj, 0,
(CPSGPShader**)&pShader->m_pCode);
if(FAILED(hr))
{
D3D_THROW_FAIL("Failed to create vertex shader code");
}
// When device driver can not handle separate fog value in the FVF,
// we should use specular alpha as the fog factor
if (pShader->m_pCode->m_dwOutFVF & D3DFVF_FOG &&
!(GetD3DCaps()->PrimitiveMiscCaps & D3DPMISCCAPS_FOGINFVF))
{
pShader->m_pCode->m_dwOutFVF &= ~D3DFVF_FOG;
// Assume that texture coordinates follow fog value
// No need to adjust offsets when specular is already present
if (pShader->m_pCode->m_dwOutFVF & D3DFVF_SPECULAR)
{
pShader->m_pCode->m_dwOutVerSize -= 4;
pShader->m_pCode->m_dwTextureOffset -= 4;
}
pShader->m_pCode->m_dwOutFVF |= D3DFVF_SPECULAR;
}
// Clear texture format bits if device can handle only 2 floats per
// texture coordinate
if (m_dwRuntimeFlags & D3DRT_ONLY2FLOATSPERTEXTURE &&
pShader->m_pCode->m_dwOutFVF & 0xFFFF0000)
{
CVShaderCode * pCode = pShader->m_pCode;
pCode->m_dwOutFVF &= 0x0000FFFF;
pCode->m_dwOutVerSize = ComputeVertexSizeFVF(pCode->m_dwOutFVF);
for (DWORD i=0; i < pCode->m_nOutTexCoord; i++)
{
pCode->m_dwOutTexCoordSize[i] = 2 * 4;
}
}
if ((LPVOID)m_pv->pGeometryFuncs != (LPVOID)GeometryFuncsGuaranteed)
{
DWORD dwOutputFVF = pShader->m_pCode->m_dwOutFVF;
CVShaderCode* pCodeMs = pShader->m_pCode;
// Now we can create PSGP shader
hr = m_pv->pGeometryFuncs->CreateShader(pDecl->m_VertexElements,
pDecl->m_dwNumElements,
pdwFunctionAdj, dwOutputFVF,
(CPSGPShader**)&pShader->m_pCode);
if(FAILED(hr))
{
delete pCodeMs;
D3D_THROW_FAIL("Failed to create vertex shader code");
}
// Copy pre-computed data from Microsoft's shader to the PSGP
CPSGPShader * pCode = pShader->m_pCode;
CPSGPShader * pMsShader = pCodeMs;
pCode->m_dwOutRegs = pMsShader->m_dwOutRegs;
pCode->m_dwOutFVF = pMsShader->m_dwOutFVF;
pCode->m_dwPointSizeOffset = pMsShader->m_dwPointSizeOffset;
pCode->m_dwDiffuseOffset = pMsShader->m_dwDiffuseOffset;
pCode->m_dwSpecularOffset = pMsShader->m_dwSpecularOffset;
pCode->m_dwFogOffset = pMsShader->m_dwFogOffset;
pCode->m_dwTextureOffset = pMsShader->m_dwTextureOffset;
pCode->m_nOutTexCoord = pMsShader->m_nOutTexCoord;
pCode->m_dwOutVerSize = pMsShader->m_dwOutVerSize;
for (DWORD i=0; i < pCode->m_nOutTexCoord; i++)
{
pCode->m_dwOutTexCoordSize[i] = pMsShader->m_dwOutTexCoordSize[i];
}
// Microsoft shader is not needed any more
delete pCodeMs;
}
}
}
else
{
if ( bIsCheckedBuild && (GetDeviceType() != D3DDEVTYPE_HAL ) )
{
// pass non-stripped version
m_pDDI->CreateVertexShader(
pdwDeclaration, dwDeclSize,
pShader->m_pOrgFuncCode,
pShader->m_OrgFuncCodeSize, dwHandle,
pShader->m_Declaration.m_bLegacyFVF);
}
else
{
// pass stripped version
m_pDDI->CreateVertexShader(
pdwDeclaration, dwDeclSize,
pShader->m_pStrippedFuncCode,
pShader->m_StrippedFuncCodeSize, dwHandle,
pShader->m_Declaration.m_bLegacyFVF);
}
}
DebugStateChanged( D3DDM_SC_VSMODIFYSHADERS );
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetVertexShaderI"
void CD3DHal::SetVertexShaderI(DWORD dwHandle)
{
#if DBG
// We need to validate shader handle here, because the shader could be
// deleted by user after creating a state block with the shader handle.
CheckVertexShaderHandle(dwHandle);
#endif
CVConstantData* pConst = NULL;
if (!D3DVSD_ISLEGACY(dwHandle))
{
CVShader* pShader = (CVShader*)m_pVShaderArray->GetObject(dwHandle);
pConst = pShader->m_Declaration.m_pConstants;
}
// Ignore redundant handle when we do not need to update constantes
if(pConst == NULL)
{
if(dwHandle == m_dwCurrentShaderHandle)
return;
}
else
{
// Load constants
while (pConst)
{
HRESULT hr;
hr = m_pv->pGeometryFuncs->LoadShaderConstants(pConst->m_dwAddress,
pConst->m_dwCount,
pConst->m_pData);
if (FAILED(hr))
{
D3D_THROW_FAIL("Failed to load vertex shader constants");
}
pConst = (CVConstantData*)pConst->m_pNext;
m_dwRuntimeFlags |= D3DRT_NEED_VSCONST_UPDATE;
}
}
ForceFVFRecompute();
// When we switch from FVF shaders to programmable we need to re-compute
// clipping planes, because they are transformed by different matrix
if (this->rstates[D3DRENDERSTATE_CLIPPLANEENABLE])
{
this->dwFEFlags |= D3DFE_CLIPPLANES_DIRTY;
}
m_dwCurrentShaderHandle = dwHandle;
if (m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING)
{
m_dwRuntimeFlags &= ~D3DRT_POINTSIZEINVERTEX;
m_dwRuntimeFlags |= D3DRT_SHADERDIRTY;
m_pv->dwDeviceFlags &= ~D3DDEV_TRANSFORMEDFVF;
if (D3DVSD_ISLEGACY(dwHandle))
{
if (dwHandle & D3DFVF_PSIZE)
m_dwRuntimeFlags |= D3DRT_POINTSIZEINVERTEX;
m_pCurrentShader = NULL;
m_pv->dwDeviceFlags &= ~(D3DDEV_STRIDE | D3DDEV_VERTEXSHADERS);
if (FVF_TRANSFORMED(dwHandle))
{
if (!(m_dwRuntimeFlags & D3DRT_EXECUTESTATEMODE))
{
m_pDDI->SetVertexShader(dwHandle);
}
m_pv->dwDeviceFlags |= D3DDEV_TRANSFORMEDFVF;
}
m_pfnPrepareToDraw = PrepareToDrawLegacy;
m_pv->dwVIDIn = dwHandle;
SetupStrides(m_pv, m_pStream[0].m_dwStride);
}
else
{
CVShader* pShader = (CVShader*)m_pVShaderArray->GetObject(dwHandle);
m_pCurrentShader = pShader;
if(!(pShader->m_dwFlags & CVShader::FIXEDFUNCTION))
{
// Programmable vertex shaders are used
m_pv->dwDeviceFlags |= D3DDEV_VERTEXSHADERS;
m_pfnPrepareToDraw = PrepareToDrawVVM;
if (m_pCurrentShader->m_pCode->m_dwOutFVF & D3DFVF_PSIZE)
m_dwRuntimeFlags |= D3DRT_POINTSIZEINVERTEX;
// Pre-compute as much info as possible and keep it
// in the vertex descriptors. This information is constant
// unless shader is changed
CVDeclaration* pDecl = &m_pCurrentShader->m_Declaration;
CVertexDesc* pVD = m_pv->VertexDesc;
CVElement *pElem = pDecl->m_VertexElements;
m_pv->dwNumUsedVertexDescs = pDecl->m_dwNumElements;
for (DWORD i = pDecl->m_dwNumElements; i; i--)
{
pVD->pfnCopy = pElem->m_pfnCopy;
pVD->dwRegister = pElem->m_dwRegister;
pVD->dwVertexOffset = pElem->m_dwOffset;
pVD->pStream = &m_pStream[pElem->m_dwStreamIndex];
pVD++;
pElem++;
}
}
else
{
// Fixed-function pipeline is used with declarations
// We draw primitives using strided code path
m_pv->dwDeviceFlags |= D3DDEV_STRIDE;
m_pv->dwDeviceFlags &= ~D3DDEV_VERTEXSHADERS;
m_pfnPrepareToDraw = PrepareToDraw;
if (pShader->m_dwInputFVF & D3DFVF_PSIZE)
m_dwRuntimeFlags |= D3DRT_POINTSIZEINVERTEX;
// Go through the elements in the current declaration and
// initialize vertex descriptors. They are used to quickly
// initialize strided data pointers.
CVDeclaration* pDecl = &m_pCurrentShader->m_Declaration;
CVertexDesc* pVD = m_pv->VertexDesc;
CVElement *pElem = pDecl->m_VertexElements;
m_pv->dwNumUsedVertexDescs = pDecl->m_dwNumElements;
for (DWORD i = pDecl->m_dwNumElements; i; i--)
{
pVD->pElement = &m_pv->elements[pElem->m_dwRegister];
pVD->pStream = &m_pStream[pElem->m_dwStreamIndex];
pVD->dwVertexOffset = pElem->m_dwOffset;
pVD++;
pElem++;
}
m_pv->dwVIDIn = pDecl->m_dwInputFVF;
if (pDecl->m_dwInputFVF & D3DFVF_PSIZE)
m_dwRuntimeFlags |= D3DRT_POINTSIZEINVERTEX;
}
HRESULT hr = m_pv->pGeometryFuncs->SetActiveShader(pShader->m_pCode);
if (FAILED(hr))
{
D3D_THROW_FAIL("Failed to set active vertex shader");
}
}
m_pDDI->PickProcessPrimitive();
}
else
{
#if DBG
// For the validation we need to set the m_pCurrentShader even for
// hardware mode
m_pv->dwDeviceFlags &= ~D3DDEV_VERTEXSHADERS;
if (D3DVSD_ISLEGACY(dwHandle))
{
m_pCurrentShader = NULL;
}
else
{
m_pCurrentShader = (CVShader*)m_pVShaderArray->GetObject(dwHandle);
if(!(m_pCurrentShader->m_dwFlags & CVShader::FIXEDFUNCTION))
{
// Programmable pipeline is used
m_pv->dwDeviceFlags |= D3DDEV_VERTEXSHADERS;
}
}
#endif
if (!(m_dwRuntimeFlags & D3DRT_EXECUTESTATEMODE))
{
m_pDDI->SetVertexShaderHW(dwHandle);
}
}
PickDrawPrimFn();
DebugStateChanged( D3DDM_SC_VSSETSHADER );
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::DeleteVertexShaderI"
void CD3DHal::DeleteVertexShaderI(DWORD dwHandle)
{
#if DBG
for(unsigned Handle = 0; Handle < m_pRTPatchValidationInfo->GetSize(); ++Handle)
{
if ((*m_pRTPatchValidationInfo)[Handle].m_pObj != 0)
{
if (static_cast<CRTPatchValidationInfo*>((*m_pRTPatchValidationInfo)[Handle].m_pObj)->m_ShaderHandle == dwHandle)
{
static_cast<CRTPatchValidationInfo*>((*m_pRTPatchValidationInfo)[Handle].m_pObj)->m_ShaderHandle = 0;
D3D_INFO(0, "Found this vertex shader in a cached patch. Will invalidate the cached patch.");
}
}
}
#endif // DBG
if (dwHandle == m_dwCurrentShaderHandle)
{
m_pCurrentShader = NULL;
m_dwCurrentShaderHandle = 0;
}
if (!D3DVSD_ISLEGACY(dwHandle))
{
CVShader* pShader = (CVShader*)m_pVShaderArray->GetObject(dwHandle);
#if DBG
if (pShader == NULL)
{
D3D_THROW(D3DERR_INVALIDCALL, "Invalid vertex shader handle");
}
#endif
if (!(pShader->m_dwFlags & CVShader::SOFTWARE))
{
m_pDDI->DeleteVertexShader(dwHandle);
}
}
DebugStateChanged( D3DDM_SC_VSMODIFYSHADERS );
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetVertexShaderConstantI"
void
CD3DHal::SetVertexShaderConstantI(DWORD Register, CONST VOID* pData, DWORD count)
{
HRESULT hr;
if (m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING ||
((count + Register) <= D3DVS_CONSTREG_MAX_V1_1))
{
// For software vertex processing we store constant registers in PSGP if
// possible
hr = m_pv->pGeometryFuncs->LoadShaderConstants(Register, count,
const_cast<VOID*>(pData));
}
else
{
if (Register >= D3DVS_CONSTREG_MAX_V1_1)
{
// When all modified registers are above software limit, we use Microsoft
// internal array
hr = GeometryFuncsGuaranteed->LoadShaderConstants(Register, count,
const_cast<VOID*>(pData));
}
else
{
// Part of constant data is stores in the PSGP array and part in the
// Microsoft's array
UINT FirstCount = D3DVS_CONSTREG_MAX_V1_1 - Register;
hr = m_pv->pGeometryFuncs->LoadShaderConstants(Register, FirstCount,
const_cast<VOID*>(pData));
if (FAILED(hr))
{
D3D_THROW(hr, "Failed to set vertex shader constants");
}
hr = GeometryFuncsGuaranteed->LoadShaderConstants(D3DVS_CONSTREG_MAX_V1_1,
Register + count - D3DVS_CONSTREG_MAX_V1_1,
&((DWORD*)pData)[FirstCount*4]);
}
}
if (FAILED(hr))
{
D3D_THROW(hr, "Failed to set vertex shader constants");
}
if (!(m_dwRuntimeFlags & D3DRT_EXECUTESTATEMODE))
{
if (m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING)
m_dwRuntimeFlags |= D3DRT_NEED_VSCONST_UPDATE;
else
m_pDDI->SetVertexShaderConstant(Register,
pData,
count);
}
DebugStateChanged( D3DDM_SC_VSCONSTANTS );
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::ValidateDraw2"
void CD3DHal::ValidateDraw2(D3DPRIMITIVETYPE primType,
UINT StartVertex,
UINT PrimitiveCount,
UINT NumVertices,
BOOL bIndexPrimitive,
UINT StartIndex)
{
#if DBG
if (this->rstates[D3DRS_FILLMODE] == D3DFILL_POINT &&
m_dwRuntimeFlags & D3DRT_POINTSIZEPRESENT &&
primType != D3DPT_POINTLIST)
{
D3D_INFO(0, "Result of drawing primitives with D3DFILL_POINT fill mode "
"and point size not equal 1.0f could be different on "
"different devices");
}
if ((m_dwHintFlags & D3DDEVBOOL_HINTFLAGS_INSCENE) == 0 &&
!(m_pv->dwFlags & D3DPV_VBCALL))
{
D3D_THROW_FAIL("Need to call BeginScene before rendering.");
}
if (m_dwCurrentShaderHandle == 0)
{
D3D_THROW_FAIL("Invalid vertex shader handle (0x0)");
}
if (bIndexPrimitive && primType == D3DPT_POINTLIST)
{
D3D_THROW_FAIL("Indexed point lists are not supported");
}
if (*(FLOAT*)&rstates[D3DRS_PATCHSEGMENTS] > 1.f)
{
if (m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING)
{
D3D_THROW_FAIL("N-Patches are not supported with software vertex processing");
}
else
if ((GetD3DCaps()->DevCaps & (D3DDEVCAPS_NPATCHES | D3DDEVCAPS_RTPATCHES)) == 0)
{
D3D_THROW_FAIL("N-Patches are not supported");
}
}
BOOL bUserMemPrimitive = this->m_dwRuntimeFlags & D3DRT_USERMEMPRIMITIVE;
if (D3DVSD_ISLEGACY(m_dwCurrentShaderHandle))
{
// DX7 FVF handles can work only from stream zero
if (!bUserMemPrimitive)
{
if (m_pStream[0].m_pVB == NULL)
{
D3D_THROW_FAIL("Stream 0 should be initialized for FVF shaders");
}
DWORD dwFVF = m_pStream[0].m_pVB->GetFVF();
if (dwFVF != 0 && dwFVF != m_dwCurrentShaderHandle)
{
D3D_THROW_FAIL("Current vertex shader doesn't match VB's FVF");
}
if (FVF_TRANSFORMED(m_dwCurrentShaderHandle))
{
if (!(m_pv->dwDeviceFlags & D3DDEV_DONOTCLIP) &&
m_pStream[0].m_pVB->GetBufferDesc()->Usage & D3DUSAGE_DONOTCLIP)
{
D3D_THROW_FAIL("Vertex buffer with D3DUSAGE_DONOTCLIP is used with clipping");
}
}
else
{
D3DVERTEXBUFFER_DESC Desc;
static_cast<IDirect3DVertexBuffer8*>(m_pStream[0].m_pVB)->GetDesc(&Desc);
if ((BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING) != 0 &&
(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING) != 0 &&
(Desc.Usage & D3DUSAGE_SOFTWAREPROCESSING) == 0 &&
Desc.Pool != D3DPOOL_SYSTEMMEM)
{
D3D_THROW_FAIL("Vertex buffer should have software usage or should be managed or should be in system memory");
}
}
if (m_pStream[0].m_pVB->IsLocked())
{
D3D_THROW_FAIL("Vertex buffer must be unlocked during DrawPrimitive call");
}
if (*((FLOAT*)&rstates[D3DRS_PATCHSEGMENTS]) > 1.f && (m_pStream[0].m_pVB->GetBufferDesc()->Usage & D3DUSAGE_NPATCHES) == 0)
{
D3D_THROW_FAIL("Vertex buffers used for rendering N-Patches should have D3DUSAGE_NPATCHES set");
}
}
// DX7 drivers cannot handle case when vertex size, computed from FVF,
// is different from the stream stride
if (m_pStream[0].m_dwStride != ComputeVertexSizeFVF(m_dwCurrentShaderHandle))
{
D3D_THROW_FAIL("Stream 0 stride should match the stride, implied by the current vertex shader");
}
if (m_pStream[0].m_dwNumVertices < (StartVertex + NumVertices))
{
D3D_THROW_FAIL("Streams do not have required number of vertices");
}
}
else
{
if (m_pv->dwDeviceFlags & D3DDEV_VERTEXSHADERS)
{
CVShaderCode * pCode = m_pCurrentShader->m_pCode;
for (DWORD i=0; i < D3DHAL_TSS_MAXSTAGES; i++)
{
if (this->tsstates[i][D3DTSS_TEXCOORDINDEX] != i)
{
D3D_ERR("Stage %d - Texture coordinate index in the stage "
"must be equal to the stage index when programmable"
" vertex pipeline is used", i);
D3D_THROW_FAIL("");
}
DWORD TexTransformFlags = tsstates[i][D3DTSS_TEXTURETRANSFORMFLAGS];
if (pCode)
{
if (TexTransformFlags & D3DTTFF_PROJECTED &&
!(m_dwRuntimeFlags & D3DRT_ONLY2FLOATSPERTEXTURE) &&
pCode->m_dwOutTexCoordSize[i] != 16)
{
D3D_ERR("Stage %d - Vertex shader must write XYZW to the "
"output texture register when texture projection is enabled", i);
D3D_THROW_FAIL("");
}
}
if ((TexTransformFlags & ~D3DTTFF_PROJECTED) != D3DTTFF_DISABLE)
{
D3D_ERR("Stage %d - Count in D3DTSS_TEXTURETRANSFORMFLAGS "
"must be 0 when programmable pipeline is used", i);
D3D_THROW_FAIL("");
}
}
}
if (m_pCurrentShader->m_Declaration.m_bStreamTessPresent)
{
D3D_THROW_FAIL("Declaration with tesselator stream cannot be used with DrawPrimitive API");
}
if (((GetDDIType() < D3DDDITYPE_DX8)&&
(m_pCurrentShader->m_Declaration.m_bLegacyFVF == FALSE))
&&
!(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING)
)
{
D3D_THROW_FAIL("Device does not support declarations");
}
// Check if
// 1. streams, referenced by the current shader, are valid
// 2. stride in the current shader and in the stream matches
// 3. Compute max number of vertices the streams can contain
CVStreamDecl* pStream;
pStream = m_pCurrentShader->m_Declaration.m_pActiveStreams;
while(pStream)
{
UINT index = pStream->m_dwStreamIndex;
CVStream* pDeviceStream = &m_pStream[index];
if (bUserMemPrimitive)
{
DXGASSERT(pDeviceStream->m_pData != NULL);
if (index != 0)
{
D3D_THROW_FAIL("DrawPrimitiveUP can use declaration only with stream 0");
}
}
else
{
if (pDeviceStream->m_pVB == NULL)
{
D3D_ERR("Stream %d is not set, but used by current declaration", index);
D3D_THROW_FAIL("");
}
if (pDeviceStream->m_pVB->IsLocked())
{
D3D_ERR("Vertex buffer in stream %d must be unlocked during drawing", index);
D3D_THROW_FAIL("");
}
D3DVERTEXBUFFER_DESC Desc;
static_cast<IDirect3DVertexBuffer8*>(pDeviceStream->m_pVB)->GetDesc(&Desc);
if ((BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING) != 0 &&
(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING) != 0 &&
(Desc.Usage & D3DUSAGE_SOFTWAREPROCESSING) == 0 &&
Desc.Pool != D3DPOOL_SYSTEMMEM)
{
D3D_INFO(0, "In stream %d vertex buffer should have software usage or should be managed or should be in system memory", pStream->m_dwStreamIndex);
D3D_THROW_FAIL("");
}
if (*((FLOAT*)&rstates[D3DRS_PATCHSEGMENTS]) > 1.f && (pDeviceStream->m_pVB->GetBufferDesc()->Usage & D3DUSAGE_NPATCHES) == 0)
{
D3D_THROW_FAIL("Vertex buffers used for rendering N-Patches should have D3DUSAGE_NPATCHES set");
}
// Validate matching of FVF in the vertex buffer and stream
// declaration
if (m_pv->dwDeviceFlags & D3DDEV_VERTEXSHADERS)
{
if (pDeviceStream->m_pVB->GetFVF() != 0)
{
D3D_INFO(1, "In stream %d vertex buffer with FVF is "
"used with programmable vertex shader",
pStream->m_dwStreamIndex);
}
}
else
{
// Fixed function pipeline case
DWORD vbFVF = pDeviceStream->m_pVB->GetFVF();
DWORD streamFVF = pStream->m_dwFVF;
// VB FVF should be a superset of the stream FVF
if (vbFVF && ((vbFVF & streamFVF) != streamFVF))
{
D3D_INFO(0, "In stream %d vertex buffer FVF and declaration FVF do not match",
pStream->m_dwStreamIndex);
}
}
}
// Stride 0 is allowed
if (pDeviceStream->m_dwStride)
{
if (pDeviceStream->m_dwStride < pStream->m_dwStride)
{
D3D_ERR("Vertex strides in stream %d is less than in the declaration", index);
D3D_THROW_FAIL("");
}
if (pDeviceStream->m_dwNumVertices < (StartVertex + NumVertices))
{
D3D_ERR("Stream %d does not have required number of vertices",
pStream->m_dwStreamIndex);
D3D_THROW_FAIL("");
}
}
pStream = (CVStreamDecl*)pStream->m_pNext;
}
}
if (bIndexPrimitive)
{
if (!bUserMemPrimitive)
{
if (m_pIndexStream->m_pVBI == NULL)
{
D3D_THROW_FAIL("Index stream is not set");
}
if (m_pIndexStream->m_pVBI->IsLocked())
{
D3D_THROW_FAIL("Index buffer must be unlocked during drawing");
}
UINT NumIndices = GETVERTEXCOUNT(primType, PrimitiveCount);
if (m_pIndexStream->m_dwNumVertices < (StartIndex + NumIndices))
{
D3D_THROW_FAIL("Index stream does not have required number of indices");
}
if (FVF_TRANSFORMED(m_dwCurrentShaderHandle) &&
D3DVSD_ISLEGACY(m_dwCurrentShaderHandle))
{
if (!(m_pv->dwDeviceFlags & D3DDEV_DONOTCLIP) &&
(m_pIndexStream->m_pVBI->GetBufferDesc()->Usage & D3DUSAGE_DONOTCLIP))
{
D3D_THROW_FAIL("Index buffer with D3DUSAGE_DONOTCLIP is used with clipping");
}
}
else
{
D3DINDEXBUFFER_DESC Desc;
static_cast<IDirect3DIndexBuffer8*>(m_pIndexStream->m_pVBI)->GetDesc(&Desc);
if ((BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING) != 0 &&
(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING) != 0 &&
(Desc.Usage & D3DUSAGE_SOFTWAREPROCESSING) == 0 &&
Desc.Pool != D3DPOOL_SYSTEMMEM)
{
D3D_THROW_FAIL("Index buffer should have software usage or should be managed or should be in system memory");
}
}
if (*((FLOAT*)&rstates[D3DRS_PATCHSEGMENTS]) > 1.f && (m_pIndexStream->m_pVBI->GetBufferDesc()->Usage & D3DUSAGE_NPATCHES) == 0)
{
D3D_THROW_FAIL("Index buffers used for rendering N-Patches should have D3DUSAGE_NPATCHES set");
}
}
else
{
DXGASSERT(m_pIndexStream->m_pData != NULL);
}
}
#endif //DBG
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::DrawPoints"
void CD3DHal::DrawPoints(UINT StartVertex)
{
BOOL bRecomputeOutputFVF = FALSE;
// If point scale is enabled and device supports point sprites
// we may need to add the point size to the output FVF
if (rstates[D3DRS_POINTSCALEENABLE] &&
!(m_dwRuntimeFlags & D3DRT_POINTSIZEINVERTEX) &&
!(m_pv->dwDeviceFlags & D3DDEV_TRANSFORMEDFVF))
{
ForceFVFRecompute();
bRecomputeOutputFVF = TRUE;
}
if (m_dwRuntimeFlags & D3DRT_DOPOINTSPRITEEMULATION)
{
// We do point sprite expansion when point size is not 1.0 in the
// render state or it is present in vertices or we need to do point
// scaling for untransformed vertices
if ((m_dwRuntimeFlags & D3DRT_POINTSIZEPRESENT ||
(rstates[D3DRS_POINTSCALEENABLE] &&
!(m_pv->dwDeviceFlags & D3DDEV_TRANSFORMEDFVF))) &&
// We do not do emulation for devices which supports point sprites,
// but only when there is no point size in the FVF
!(bRecomputeOutputFVF == FALSE &&
(m_dwRuntimeFlags & D3DRT_POINTSIZEINVERTEX) == 0 &&
m_dwRuntimeFlags & D3DRT_SUPPORTSPOINTSPRITES))
{
m_pv->dwDeviceFlags |= D3DDEV_DOPOINTSPRITEEMULATION;
m_pDDI->PickProcessPrimitive();
}
else
{
if (m_pv->dwDeviceFlags & D3DDEV_TRANSFORMEDFVF &&
(m_pv->dwDeviceFlags & D3DDEV_DONOTCLIP ||
!(m_pv->dwDeviceFlags & D3DDEV_VBPROCVER)))
{
// Now we can call DDI directly, because no emulation is
// necessary
if (m_pStream[0].m_pVB)
{
(*m_pDDI->GetDrawPrimFunction())(this, m_pv->primType,
StartVertex,
m_pv->dwNumPrimitives);
}
else
{
m_pDDI->SetVertexShader(m_dwCurrentShaderHandle);
m_pDDI->SetStreamSource(0, &m_pStream[0]);
m_pDDI->DrawPrimitiveUP(m_pv->primType, m_pv->dwNumPrimitives);
}
return;
}
}
}
(this->*m_pfnPrepareToDraw)(StartVertex);
(m_pDDI->*m_pDDI->m_pfnProcessPrimitive)(m_pv, StartVertex);
if (bRecomputeOutputFVF)
{
ForceFVFRecompute();
}
m_pv->dwDeviceFlags &= ~D3DDEV_DOPOINTSPRITEEMULATION;
m_pDDI->PickProcessPrimitive();
}
//-----------------------------------------------------------------------------
// Draw all primitive types except points
//
#undef DPF_MODNAME
#define DPF_MODNAME "DrawPrimitiveHal"
void CD3DHal_DrawPrimitive(CD3DBase* pBaseDevice, D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex, UINT PrimitiveCount)
{
CD3DHal* pDevice = static_cast<CD3DHal*>(pBaseDevice);
CD3DDDIDX6* pDDI = pBaseDevice->m_pDDI;
#if DBG
UINT nVer = GETVERTEXCOUNT(PrimitiveType, PrimitiveCount);
pDevice->ValidateDraw2(PrimitiveType, StartVertex, PrimitiveCount, nVer,
FALSE);
#endif
D3DFE_PROCESSVERTICES* pv = pDevice->m_pv;
pv->primType = PrimitiveType;
pv->dwNumPrimitives = PrimitiveCount;
pv->dwNumVertices = GETVERTEXCOUNT(PrimitiveType, PrimitiveCount);
pv->dwFlags &= D3DPV_PERSIST;
(pDevice->*pDevice->m_pfnPrepareToDraw)(StartVertex);
(pDDI->*pDDI->m_pfnProcessPrimitive)(pv, StartVertex);
}
//-----------------------------------------------------------------------------
// Draw only points
//
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::DrawPointsI"
void CD3DHal::DrawPointsI(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex,
UINT PrimitiveCount)
{
#if DBG
UINT nVer = GETVERTEXCOUNT(PrimitiveType, PrimitiveCount);
ValidateDraw2(PrimitiveType, StartVertex, PrimitiveCount, nVer, FALSE);
#endif
if (!(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING) ||
CanCallDriver(this, PrimitiveType))
{
(*m_pfnDrawPrim)(this, PrimitiveType, StartVertex, PrimitiveCount);
}
else
{
m_pv->primType = PrimitiveType;
m_pv->dwNumPrimitives = PrimitiveCount;
m_pv->dwNumVertices = GETVERTEXCOUNT(PrimitiveType, PrimitiveCount);
m_pv->dwFlags &= D3DPV_PERSIST;
DrawPoints(StartVertex);
}
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal_DrawIndexedPrimitive"
void CD3DHal_DrawIndexedPrimitive(CD3DBase* pBaseDevice,
D3DPRIMITIVETYPE PrimitiveType,
UINT BaseIndex,
UINT MinIndex, UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount)
{
CD3DHal* pDevice = static_cast<CD3DHal*>(pBaseDevice);
CVIndexStream* pIndexStream = pBaseDevice->m_pIndexStream;
CD3DDDIDX6* pDDI = pBaseDevice->m_pDDI;
#if DBG
pDevice->ValidateDraw2(PrimitiveType, MinIndex + pIndexStream->m_dwBaseIndex,
PrimitiveCount, NumVertices, TRUE, StartIndex);
#endif
D3DFE_PROCESSVERTICES* pv = pDevice->m_pv;
pIndexStream->m_pData = NULL;
pv->primType = PrimitiveType;
pv->dwNumPrimitives = PrimitiveCount;
pv->dwFlags &= D3DPV_PERSIST;
pv->dwNumVertices = NumVertices;
pv->dwNumIndices = GETVERTEXCOUNT(PrimitiveType, PrimitiveCount);
pv->dwIndexSize = pIndexStream->m_dwStride;
UINT StartVertex = MinIndex + pIndexStream->m_dwBaseIndex;
pDDI->SetIndexedPrimParams(StartIndex, MinIndex, NumVertices,
pIndexStream->m_dwBaseIndex);
(pDevice->*pDevice->m_pfnPrepareToDraw)(StartVertex);
(pDDI->*pDDI->m_pfnProcessIndexedPrimitive)(pv, StartVertex);
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::DrawPrimitiveUPI"
void CD3DHal::DrawPrimitiveUPI(D3DPRIMITIVETYPE PrimType, UINT PrimCount)
{
#if DBG
UINT nVer = GETVERTEXCOUNT(PrimType, PrimCount);
ValidateDraw2(PrimType, 0, PrimCount, nVer, FALSE);
#endif
m_pv->dwDeviceFlags &= ~D3DDEV_VBPROCVER;
if (!(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING))
{
if (m_dwRuntimeFlags & D3DRT_DONPATCHCONVERSION &&
PrimType >= D3DPT_TRIANGLELIST)
{
CD3DHal_DrawNPatch(this, PrimType, 0, PrimCount);
}
else
{
m_pDDI->DrawPrimitiveUP(PrimType, PrimCount);
}
}
else
if (CanCallDriver(this, PrimType))
{
m_pDDI->SetVertexShader(m_dwCurrentShaderHandle);
m_pDDI->SetStreamSource(0, &m_pStream[0]);
m_pDDI->DrawPrimitiveUP(PrimType, PrimCount);
}
else
{
SetupStrides(m_pv, m_pStream[0].m_dwStride);
m_pv->primType = PrimType;
m_pv->dwNumPrimitives = PrimCount;
m_pv->dwNumVertices = GETVERTEXCOUNT(PrimType, PrimCount);
m_pv->dwFlags &= D3DPV_PERSIST;
if (PrimType != D3DPT_POINTLIST)
{
(this->*m_pfnPrepareToDraw)(0);
(m_pDDI->*m_pDDI->m_pfnProcessPrimitive)(m_pv, 0);
}
else
DrawPoints(0);
}
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::DrawIndexedPrimitiveUPI"
void
CD3DHal::DrawIndexedPrimitiveUPI(D3DPRIMITIVETYPE PrimType,
UINT MinVertexIndex,
UINT NumVertices,
UINT PrimCount)
{
#if DBG
ValidateDraw2(PrimType, 0, PrimCount, NumVertices, TRUE);
#endif
m_pv->dwDeviceFlags &= ~D3DDEV_VBPROCVER;
if (!(m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING))
{
if (m_dwRuntimeFlags & D3DRT_DONPATCHCONVERSION &&
PrimType >= D3DPT_TRIANGLELIST)
{
CD3DHal_DrawIndexedNPatch(this, PrimType, 0, MinVertexIndex,
NumVertices, 0, PrimCount);
}
else
{
m_pDDI->DrawIndexedPrimitiveUP(PrimType, MinVertexIndex, NumVertices,
PrimCount);
}
}
else
if (CanCallDriver(this, PrimType))
{
m_pDDI->SetVertexShader(m_dwCurrentShaderHandle);
m_pDDI->SetStreamSource(0, &m_pStream[0]);
m_pDDI->SetIndices(m_pIndexStream);
m_pDDI->DrawIndexedPrimitiveUP(PrimType, MinVertexIndex, NumVertices,
PrimCount);
}
else
{
SetupStrides(m_pv, m_pStream[0].m_dwStride);
m_pv->primType = PrimType;
m_pv->dwNumPrimitives = PrimCount;
m_pv->dwFlags &= D3DPV_PERSIST;
m_pv->dwNumVertices = NumVertices;
m_pv->dwNumIndices = GETVERTEXCOUNT(PrimType, PrimCount);
m_pv->lpwIndices = (WORD*)m_pIndexStream->m_pData;
m_pv->dwIndexSize = m_pIndexStream->m_dwStride;
m_pDDI->SetIndexedPrimParams(0, MinVertexIndex,
MinVertexIndex + NumVertices, 0);
(this->*m_pfnPrepareToDraw)(MinVertexIndex);
(m_pDDI->*m_pDDI->m_pfnProcessIndexedPrimitive)(m_pv, MinVertexIndex);
}
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "SetupFVFDataVVM"
void SetupFVFDataVVM(CD3DHal* pDev)
{
D3DFE_PROCESSVERTICES* pv = pDev->m_pv;
// We have to restore texture stage indices if previous primitive
// re-mapped them
if (pv->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
{
RestoreTextureStages(pDev);
}
// Input FVF has no meaning for vertex shaders, but it is used for validaion
pv->dwVIDIn = 0;
// Compute output FVF
CVShaderCode * pCode = pDev->m_pCurrentShader->m_pCode;
pv->dwVIDOut = pCode->m_dwOutFVF;
pv->dwOutputSize = pCode->m_dwOutVerSize;
pv->nOutTexCoord = pCode->m_nOutTexCoord;
// We use offsets, computed by the vertex shader
pv->pointSizeOffsetOut = pCode->m_dwPointSizeOffset;
pv->diffuseOffsetOut = pCode->m_dwDiffuseOffset;
pv->specularOffsetOut = pCode->m_dwSpecularOffset;
pv->fogOffsetOut = pCode->m_dwFogOffset;
pv->texOffsetOut = pCode->m_dwTextureOffset;
pv->dwTextureCoordSizeTotal = 0;
for (DWORD i=0; i < pv->nOutTexCoord; i++)
{
DWORD dwSize = pCode->m_dwOutTexCoordSize[i];
pv->dwTextureCoordSize[i] = dwSize;
pv->dwTextureCoordSizeTotal += dwSize;
}
}
//----------------------------------------------------------------------
void CD3DHal::SetupFVFData()
{
CD3DHal::SetupFVFDataCommon();
if (!(m_pv->dwVIDIn & D3DFVF_NORMAL))
m_pv->dwDeviceFlags &= ~D3DDEV_NORMALINCAMERASPACE;
}
//---------------------------------------------------------------------
// Computes the following data
// - dwTextureCoordOffset[] offset of every input texture coordinates
static __inline void ComputeInpTexCoordOffsets(DWORD dwNumTexCoord,
DWORD dwFVF,
DWORD *pdwTextureCoordOffset)
{
// Compute texture coordinate size
DWORD dwTextureFormats = dwFVF >> 16;
if (dwTextureFormats == 0)
{
for (DWORD i=0; i < dwNumTexCoord; i++)
{
pdwTextureCoordOffset[i] = i << 3;
}
}
else
{
DWORD dwOffset = 0;
for (DWORD i=0; i < dwNumTexCoord; i++)
{
pdwTextureCoordOffset[i] = dwOffset;
dwOffset += g_TextureSize[dwTextureFormats & 3];
dwTextureFormats >>= 2;
}
}
return;
}
//---------------------------------------------------------------------
// Returns 2 bits of FVF texture format for the texture index
//
static inline DWORD FVFGetTextureFormat(DWORD dwFVF, DWORD dwTextureIndex)
{
return (dwFVF >> (dwTextureIndex*2 + 16)) & 3;
}
//---------------------------------------------------------------------
// Returns texture format bits shifted to the right place
//
static inline DWORD FVFMakeTextureFormat(DWORD dwNumberOfCoordinates, DWORD dwTextureIndex)
{
return g_dwTextureFormat[dwNumberOfCoordinates] << ((dwTextureIndex << 1) + 16);
}
//---------------------------------------------------------------------
inline DWORD GetOutTexCoordSize(DWORD *pdwStage, DWORD dwInpTexCoordSize)
{
// Low byte has texture coordinate count
const DWORD dwTextureTransformFlags = pdwStage[D3DTSS_TEXTURETRANSFORMFLAGS] & 0xFF;
if (dwTextureTransformFlags == 0)
return dwInpTexCoordSize;
else
return (dwTextureTransformFlags << 2);
}
//----------------------------------------------------------------------
// pDevI->nOutTexCoord should be initialized to the number of input texture coord sets
//
void EvalTextureTransforms(LPD3DHAL pDevI, DWORD dwTexTransform,
DWORD *pdwOutTextureSize, DWORD *pdwOutTextureFormat)
{
D3DFE_PROCESSVERTICES* pv = pDevI->m_pv;
DWORD dwOutTextureSize = 0; // Used to compute output vertex size
DWORD dwOutTextureFormat = 0; // Used to compute output texture FVF
// The bits are used to find out how the texture coordinates are used.
const DWORD __USED_BY_TRANSFORM = 1;
const DWORD __USED = 2;
const DWORD __USED_TEXTURE_PROJECTION = 4;
// The low 16 bits are for _USED bits. The high 16 bits will hold
// re-mapped texture index for a stage
DWORD dwTexCoordUsage[D3DDP_MAXTEXCOORD];
memset(dwTexCoordUsage, 0, sizeof(dwTexCoordUsage));
// Re-mapping buffer will contain only stages that use texture
// This variable is used to count them
pDevI->dwNumTextureStagesToRemap = 0;
DWORD dwNewIndex = 0; // Used to generate output index
// We need offsets for every input texture coordinate, because
// we could access them in random order.
// Offsets are not needed for strided input
DWORD dwTextureCoordOffset[D3DDP_MAXTEXCOORD];
if (!(pv->dwDeviceFlags & D3DDEV_STRIDE))
{
ComputeInpTexCoordOffsets(pv->nTexCoord, pv->dwVIDIn, dwTextureCoordOffset);
}
DWORD dwOutTextureCoordSize[D3DDP_MAXTEXCOORD];
// TRUE, if we do not do texture projection and transform for a stage,
// because the stage does not have corresponding texture coordinates in the
// input
BOOL bIgnoreTexCoord = FALSE;
// Go through all texture stages and find those wich use texture coordinates
for (DWORD i=0; i < D3DDP_MAXTEXCOORD; i++)
{
if (pDevI->tsstates[i][D3DTSS_COLOROP] == D3DTOP_DISABLE)
break;
DWORD dwIndex = pDevI->tsstates[i][D3DTSS_TEXCOORDINDEX];
DWORD dwInpTextureFormat;
DWORD dwInpTexSize;
LPD3DFE_TEXTURESTAGE pStage = &pDevI->textureStageToRemap[pDevI->dwNumTextureStagesToRemap];
DWORD dwTexGenMode = dwIndex & ~0xFFFF;
pStage->dwInpOffset = 0;
dwIndex = dwIndex & 0xFFFF; // Remove texture generation mode
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACENORMAL ||
dwTexGenMode == D3DTSS_TCI_CAMERASPACEPOSITION ||
dwTexGenMode == D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR)
{
dwInpTextureFormat = D3DFVF_TEXCOORDSIZE3(dwIndex);
dwInpTexSize = 3*sizeof(D3DVALUE);
pv->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR)
pv->dwDeviceFlags |= D3DDEV_NORMALINCAMERASPACE | D3DDEV_POSITIONINCAMERASPACE;
else
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACENORMAL)
pv->dwDeviceFlags |= D3DDEV_NORMALINCAMERASPACE;
else
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACEPOSITION)
pv->dwDeviceFlags |= D3DDEV_POSITIONINCAMERASPACE;
}
else
{
if (dwIndex >= pv->nTexCoord)
{
// This could happen when input vertex does not have texture
// coordinates, but it is OK, because texture pointer in the
// stage could be NULL, or the stage does not use texture, or
// pixel shader is used.
// It is too complex and error prone to check all cases when
// this is an user error, so we just make this case to work.
dwIndex = 0;
dwInpTexSize = sizeof(float)*2;
dwInpTextureFormat = 0;
// Ignore special texture coordinate processing for this stage
bIgnoreTexCoord = TRUE;
// Disable texture transform for the stage
dwTexTransform &= ~1;
pStage->dwInpOffset = 0;
}
else
{
dwInpTexSize = pv->dwTextureCoordSize[dwIndex];
dwInpTextureFormat = FVFGetTextureFormat(pv->dwVIDIn, dwIndex);
pStage->dwInpOffset = dwTextureCoordOffset[dwIndex];
}
}
pStage->dwInpCoordIndex = dwIndex;
pStage->dwTexGenMode = dwTexGenMode;
pStage->dwOrgStage = i;
pStage->bDoTextureProjection = FALSE;
DWORD dwOutTexCoordSize; // Size of the texture coord set in bytes for this stage
if (dwTexTransform & 1)
{
pv->dwDeviceFlags |= D3DDEV_TEXTURETRANSFORM;
pStage->pmTextureTransform = &pv->mTexture[i];
dwOutTexCoordSize = GetOutTexCoordSize((DWORD*)&pDevI->tsstates[i], dwInpTexSize);
// If we have to add or remove some coordinates we go through
// the re-mapping path
if (dwOutTexCoordSize != dwInpTexSize)
pv->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
pStage->dwTexTransformFuncIndex = MakeTexTransformFuncIndex
(dwInpTexSize >> 2, dwOutTexCoordSize >> 2);
}
else
{
pStage->pmTextureTransform = NULL;
dwOutTexCoordSize = dwInpTexSize;
pStage->dwTexTransformFuncIndex = 0;
}
if (NeedTextureProjection(pv, i) && !bIgnoreTexCoord)
{
// Remove one float from the output
dwOutTexCoordSize -= 4;
// Set re-mapping so we do not complicate simple case
pv->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
// Texture projection is required for the stage
pStage->bDoTextureProjection = TRUE;
}
if ((dwTexCoordUsage[dwIndex] & 0xFFFF) == 0)
{
// Texture coordinate set is used first time
if (dwTexTransform & 1)
dwTexCoordUsage[dwIndex] |= __USED_BY_TRANSFORM;
dwTexCoordUsage[dwIndex] |= __USED;
if (pStage->bDoTextureProjection)
dwTexCoordUsage[dwIndex] |= __USED_TEXTURE_PROJECTION;
}
else
{
// Texture coordinate set is used second or more time
if (dwTexTransform & 1)
{
// This set is used by two texture transforms or a
// texture transform and without it, so we have to
// generate an additional output texture coordinate
dwTexCoordUsage[dwIndex] |= __USED_BY_TRANSFORM;
pv->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
}
else
{
if (dwTexCoordUsage[dwIndex] & __USED_BY_TRANSFORM)
{
// This set is used by two texture transforms or a
// texture transform and without it, so we have to
// generate an additional output texture coordinate
pv->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
}
else
// We can re-use the same input texture coordinate if there is no
// texture generation and texture projection flag is the same for both
// stages
if (dwTexGenMode == 0 &&
(pStage->bDoTextureProjection == ((dwTexCoordUsage[dwIndex] & __USED_TEXTURE_PROJECTION) != 0)))
{
DWORD dwOutIndex = dwTexCoordUsage[dwIndex] >> 16;
pStage->dwOutCoordIndex = dwOutIndex;
// Mark the stage as not to be used in the vertex processing loop
pStage->dwInpOffset = 0xFFFFFFFF;
goto l_NoNewOutTexCoord;
}
}
}
// If we are here, we have to generate new output texture coordinate set
pStage->dwOutCoordIndex = dwNewIndex;
dwTexCoordUsage[dwIndex] |= dwNewIndex << 16;
dwOutTextureSize += dwOutTexCoordSize;
dwOutTextureCoordSize[dwNewIndex] = dwOutTexCoordSize;
dwOutTextureFormat |= FVFMakeTextureFormat(dwOutTexCoordSize >> 2, dwNewIndex);
dwNewIndex++;
l_NoNewOutTexCoord:
pDevI->dwNumTextureStagesToRemap++;
dwTexTransform >>= 1;
}
if (pv->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
{
// Now, when we have to do re-mapping, we have to set new output texture
// coordinate set sizes and we need to remove stages, which do not produce
// output texture coordinates.
DWORD dwNumTextureStages = 0;
for (DWORD i=0; i < pDevI->dwNumTextureStagesToRemap; i++)
{
if (pDevI->textureStageToRemap[i].dwInpOffset != 0xFFFFFFFF)
{
pv->textureStage[dwNumTextureStages] = pDevI->textureStageToRemap[i];
pv->dwTextureCoordSize[dwNumTextureStages] = dwOutTextureCoordSize[dwNumTextureStages];
dwNumTextureStages++;
}
pv->dwNumTextureStages = dwNumTextureStages;
}
pv->nOutTexCoord = dwNewIndex;
}
*pdwOutTextureSize = dwOutTextureSize;
*pdwOutTextureFormat = dwOutTextureFormat;
}
//----------------------------------------------------------------------
// Sets texture transform pointer for every input texture coordinate set
//
void SetupTextureTransforms(LPD3DHAL pDevI)
{
D3DFE_PROCESSVERTICES* pv = pDevI->m_pv;
// Set texture transforms to NULL in case when some texture coordinates
// are not used by texture stages
memset(pv->pmTexture, 0, sizeof(pv->pmTexture));
for (DWORD i=0; i < pDevI->dwNumTextureStagesToRemap; i++)
{
LPD3DFE_TEXTURESTAGE pStage = &pDevI->textureStageToRemap[i];
pv->pmTexture[pStage->dwInpCoordIndex] = pStage->pmTextureTransform;
}
}
//----------------------------------------------------------------------
// Computes the following device data
// - dwVIDOut, based on input FVF id and device settings
// - nTexCoord
// - dwTextureCoordSizeTotal
// - dwTextureCoordSize[] array, based on the input FVF id
// - dwOutputSize, based on the output FVF id
//
// The function is called from ProcessVertices and DrawPrimitives code paths
//
// The following variables should be set in the pDevI:
// - dwVIDIn
//
// Number of texture coordinates is set based on dwVIDIn. ValidateFVF should
// make sure that it is not greater than supported by the driver
// Last settings for dwVIDOut and dwVIDIn are saved to speed up processing
//
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::SetupFVFDataCommon"
void CD3DHal::SetupFVFDataCommon()
{
HRESULT ret;
this->dwFEFlags &= ~D3DFE_FVF_DIRTY;
// We have to restore texture stage indices if previous primitive
// re-mapped them
if (m_pv->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
{
RestoreTextureStages(this);
}
// Compute number of the input texture coordinates
m_pv->nTexCoord = FVF_TEXCOORD_NUMBER(m_pv->dwVIDIn);
// Compute size of input texture coordinates
m_pv->dwTextureCoordSizeTotal = ComputeTextureCoordSize(m_pv->dwVIDIn,
m_pv->dwInpTextureCoordSize);
// This size is the same for input and output FVFs in case when we do not have to
// expand number of texture coordinates
for (DWORD i=0; i < m_pv->nTexCoord; i++)
m_pv->dwTextureCoordSize[i] = m_pv->dwInpTextureCoordSize[i];
m_pv->nOutTexCoord = m_pv->nTexCoord;
// Setup input vertex offsets
UpdateGeometryLoopData(m_pv);
if (FVF_TRANSFORMED(m_pv->dwVIDIn))
{
// Set up vertex pointers
m_pv->dwVIDOut = m_pv->dwVIDIn;
ComputeOutputVertexOffsets(m_pv);
m_pv->dwOutputSize = ComputeVertexSizeFVF(m_pv->dwVIDOut);
return;
}
// Compute output FVF
m_pv->dwVIDOut = D3DFVF_XYZRHW;
if (m_pv->dwDeviceFlags & D3DDEV_DONOTSTRIPELEMENTS &&
!(m_pv->dwFlags & D3DPV_VBCALL))
{
m_pv->dwVIDOut |= D3DFVF_DIFFUSE | D3DFVF_SPECULAR;
}
else
{
// If normal present we have to compute specular and duffuse
// Otherwise set these bits the same as input.
// Not that normal should not be present for XYZRHW position type
if (m_pv->dwDeviceFlags & D3DDEV_LIGHTING)
m_pv->dwVIDOut |= D3DFVF_DIFFUSE | D3DFVF_SPECULAR;
else
m_pv->dwVIDOut |= m_pv->dwVIDIn &
(D3DFVF_DIFFUSE | D3DFVF_SPECULAR);
// Always set specular flag if vertex fog is enabled
if (this->rstates[D3DRENDERSTATE_FOGENABLE] &&
m_pv->lighting.fog_mode != D3DFOG_NONE)
{
m_pv->dwVIDOut |= D3DFVF_SPECULAR;
}
else
// Clear specular flag if specular disabled and we do not have
// specular in the input
if (!this->rstates[D3DRENDERSTATE_SPECULARENABLE] &&
!(m_pv->dwVIDIn & D3DFVF_SPECULAR))
{
m_pv->dwVIDOut &= ~D3DFVF_SPECULAR;
}
}
if (m_pv->dwVIDIn & D3DFVF_PSIZE ||
m_pv->primType == D3DPT_POINTLIST &&
this->rstates[D3DRS_POINTSCALEENABLE])
{
m_pv->dwVIDOut |= D3DFVF_PSIZE;
}
// Compute number of the output texture coordinates
// Transform enable bits
m_pv->dwDeviceFlags &= ~D3DDEV_TEXTURETRANSFORM;
DWORD dwTexTransform = m_pv->dwFlags2 & __FLAGS2_TEXTRANSFORM;
// When texture transform is enabled or we need to do projected texture
// emulation or texture coordinates are taken from the vertex data (texgen),
// output texture coordinates could be generated.
// So we go and evaluate texture stages
if ((m_pv->dwFlags2 & (__FLAGS2_TEXTRANSFORM | __FLAGS2_TEXPROJ)
&& (m_pv->nTexCoord > 0)) ||
m_pv->dwFlags2 & __FLAGS2_TEXGEN)
{
DWORD dwOutTextureSize; // Used to compute output vertex size
DWORD dwOutTextureFormat; // Used to compute output texture FVF
// There are texture transforms.
// Now we find out if some of the texture coordinates are used two
// or more times and used by a texture transform. In this case we
// have expand number of output texture coordinates.
EvalTextureTransforms(this, dwTexTransform,
&dwOutTextureSize,
&dwOutTextureFormat);
if (m_pv->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
{
// For ProcessVertices calls user should set texture stages and
// wrap modes himself
if (!(m_pv->dwFlags & D3DPV_VBCALL))
{
// dwVIDIn is used to force re-compute FVF in the
// SetTextureStageState. so we save and restore it.
DWORD dwVIDInSaved = m_pv->dwVIDIn;
// Re-map indices in the texture stages and wrap modes
DWORD dwOrgWrapModes[D3DDP_MAXTEXCOORD];
memcpy(dwOrgWrapModes, &this->rstates[D3DRENDERSTATE_WRAP0],
sizeof(dwOrgWrapModes));
for (DWORD i=0; i < this->dwNumTextureStagesToRemap; i++)
{
LPD3DFE_TEXTURESTAGE pStage = &this->textureStageToRemap[i];
DWORD dwOutIndex = pStage->dwOutCoordIndex;
DWORD dwInpIndex = pStage->dwInpCoordIndex;
if (dwOutIndex != dwInpIndex || pStage->dwTexGenMode)
{
DWORD dwState = D3DRENDERSTATE_WRAP0 + dwOutIndex;
pStage->dwOrgWrapMode = dwOrgWrapModes[dwOutIndex];
DWORD dwValue = dwOrgWrapModes[dwInpIndex];
// We do not call UpdateInternaState because it
// will call ForceRecomputeFVF and we do not want this.
this->rstates[dwState] = dwValue;
m_pDDI->SetRenderState((D3DRENDERSTATETYPE)dwState, dwValue);
// We do not call UpdateInternalTextureStageState because it
// will call ForceRecomputeFVF and we do not want this.
m_pDDI->SetTSS(pStage->dwOrgStage, D3DTSS_TEXCOORDINDEX, dwOutIndex);
// We do not call UpdateInternalTextureStageState because it
// will call ForceRecomputeFVF and we do not want this.
// We set some invalid value to the internal array, because otherwise
// a new SetTextureStageState could be filtered as redundant
tsstates[pStage->dwOrgStage][D3DTSS_TEXCOORDINDEX] = 0xFFFFFFFF;
}
}
m_pv->dwVIDIn = dwVIDInSaved;
}
else
{
}
m_pv->dwVIDOut |= dwOutTextureFormat;
m_pv->dwTextureCoordSizeTotal = dwOutTextureSize;
}
else
{ // We do not do re-mapping but we have to make correspondence between
// texture sets and texture transforms
SetupTextureTransforms(this);
// Copy input texture formats
m_pv->dwVIDOut |= m_pv->dwVIDIn & 0xFFFF0000;
}
}
else
{
// Copy input texture formats
m_pv->dwVIDOut |= m_pv->dwVIDIn & 0xFFFF0000;
// When we have texture coordinate set with number of floats different
// from 2 and device does not support them, we "fix" the texture format
if (m_pv->dwVIDOut & 0xFFFF0000)
{
if (m_dwRuntimeFlags & D3DRT_ONLY2FLOATSPERTEXTURE)
{
m_pv->dwVIDOut &= ~0xFFFF0000;
for (DWORD i=0; i < m_pv->nOutTexCoord; i++)
m_pv->dwTextureCoordSize[i] = 8;
m_pv->dwTextureCoordSizeTotal = m_pv->nTexCoord * 8;
}
}
}
if (m_pv->dwDeviceFlags & D3DDEV_DONOTSTRIPELEMENTS)
{
if (m_pv->nOutTexCoord == 0 && !(m_pv->dwFlags & D3DPV_VBCALL))
{
m_pv->dwTextureCoordSize[0] = 0;
m_pv->dwVIDOut |= (1 << D3DFVF_TEXCOUNT_SHIFT);
}
}
// Set up number of output texture coordinates
m_pv->dwVIDOut |= (m_pv->nOutTexCoord << D3DFVF_TEXCOUNT_SHIFT);
if ((m_pv->dwVIDOut & 0xFFFF0000) &&
(GetDDIType() < D3DDDITYPE_DX7))
{
D3D_THROW_FAIL("Texture format bits in the output FVF for this device should be 0");
}
if (!(m_pv->dwFlags & D3DPV_VBCALL))
{
m_pv->dwOutputSize = ComputeVertexSizeFVF(m_pv->dwVIDOut);
ComputeOutputVertexOffsets(m_pv);
}
// In case if COLORVERTEX is TRUE, the vertexAlpha could be overriden
// by vertex alpha
m_pv->lighting.alpha = (DWORD)m_pv->lighting.materialAlpha;
m_pv->lighting.alphaSpecular = (DWORD)m_pv->lighting.materialAlphaS;
this->dwFEFlags |= D3DFE_VERTEXBLEND_DIRTY | D3DFE_FRONTEND_DIRTY;
}
//-----------------------------------------------------------------------------
// Sets input vertex pointers and output offsets for legacy vertex shaders for
// the programmable vertex shaders
//
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::PrepareToDrawVVM"
void CD3DHal::PrepareToDrawVVM(UINT StartVertex)
{
if (m_dwRuntimeFlags & D3DRT_SHADERDIRTY)
{
SetupFVFDataVVM(this);
m_dwRuntimeFlags &= ~D3DRT_SHADERDIRTY;
m_pDDI->SetVertexShader(m_pv->dwVIDOut);
}
// Initialize vertex pointers used in the vertex loop
CVertexDesc* pVD = m_pv->VertexDesc;
for (DWORD i = m_pv->dwNumUsedVertexDescs; i; i--)
{
CVStream* pStream = pVD->pStream;
DWORD dwStride = pStream->m_dwStride;
pVD->pMemory = pStream->Data() + pVD->dwVertexOffset +
StartVertex * dwStride;
pVD->dwStride = dwStride;
pVD++;
}
}
//-----------------------------------------------------------------------------
// Sets input vertex pointers and output offsets for legacy vertex shaders
//
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::PrepareToDrawLegacy"
void CD3DHal::PrepareToDrawLegacy(UINT StartVertex)
{
// For legacy FVFs we draw using Stream[0]
m_pv->position.lpvData = m_pStream[0].Data() +
m_pStream[0].m_dwStride * StartVertex;
if (m_dwRuntimeFlags & D3DRT_SHADERDIRTY)
{
SetupFVFData();
m_pDDI->SetVertexShader(m_pv->dwVIDOut);
m_dwRuntimeFlags &= ~D3DRT_SHADERDIRTY;
}
}
//-----------------------------------------------------------------------------
// Sets input vertex pointers and output offsets for the fixed-function pipeline
// and non-legacy vertex declarations
//
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::PrepareToDraw"
void CD3DHal::PrepareToDraw(UINT StartVertex)
{
// Initialize strided data pointers used in the vertex loop
#if DBG
{
// Set all NULL pointers to check if they are initialized by the
// declaration
for (DWORD i=0; i < __NUMELEMENTS; i++)
{
m_pv->elements[i].lpvData = NULL;
}
}
#endif
CVertexDesc* pVD = m_pv->VertexDesc;
for (DWORD i = m_pv->dwNumUsedVertexDescs; i; i--)
{
CVStream* pStream = pVD->pStream;
DWORD dwStride = pStream->m_dwStride;
pVD->pElement->lpvData = pStream->Data() +
pVD->dwVertexOffset +
StartVertex * dwStride;
pVD->pElement->dwStride = dwStride;
pVD++;
}
if (m_dwRuntimeFlags & D3DRT_SHADERDIRTY)
{
SetupFVFData();
m_pDDI->SetVertexShader(m_pv->dwVIDOut);
m_dwRuntimeFlags &= ~D3DRT_SHADERDIRTY;
}
}
//-----------------------------------------------------------------------------
//
// Object implementations
//
//---------------------------------------------------------------------
const DWORD CVShader::FIXEDFUNCTION = 1;
const DWORD CVShader::SOFTWARE = 2;
void CheckForNull(LPVOID p, DWORD line, char* file)
{
if (p == NULL)
D3D_THROW_LINE(E_OUTOFMEMORY, "Not enough memory", line, file);
}
//-----------------------------------------------------------------------------
void Copy_FLOAT1(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
pVertexRegister->x = *(float*)pInputStream;
pVertexRegister->y = 0;
pVertexRegister->z = 0;
pVertexRegister->w = 1;
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_FLOAT2(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
pVertexRegister->x = ((float*)pInputStream)[0];
pVertexRegister->y = ((float*)pInputStream)[1];
pVertexRegister->z = 0;
pVertexRegister->w = 1;
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_FLOAT3(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
pVertexRegister->x = ((float*)pInputStream)[0];
pVertexRegister->y = ((float*)pInputStream)[1];
pVertexRegister->z = ((float*)pInputStream)[2];
pVertexRegister->w = 1;
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_FLOAT4(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
pVertexRegister->x = ((float*)pInputStream)[0];
pVertexRegister->y = ((float*)pInputStream)[1];
pVertexRegister->z = ((float*)pInputStream)[2];
pVertexRegister->w = ((float*)pInputStream)[3];
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_D3DCOLOR(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
const float scale = 1.0f/255.f;
for (UINT i=0; i < count; i++)
{
const DWORD v = ((DWORD*)pInputStream)[0];
pVertexRegister->x = scale * RGBA_GETRED(v);
pVertexRegister->y = scale * RGBA_GETGREEN(v);
pVertexRegister->z = scale * RGBA_GETBLUE(v);
pVertexRegister->w = scale * RGBA_GETALPHA(v);
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_UBYTE4(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
const BYTE* v = (BYTE*)pInputStream;
pVertexRegister->x = v[0];
pVertexRegister->y = v[1];
pVertexRegister->z = v[2];
pVertexRegister->w = v[3];
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_SHORT2(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
const short* v = (short*)pInputStream;
pVertexRegister->x = v[0];
pVertexRegister->y = v[1];
pVertexRegister->z = 0;
pVertexRegister->w = 1;
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
void Copy_SHORT4(LPVOID pInputStream, UINT stride, UINT count,
VVM_WORD * pVertexRegister)
{
for (UINT i=0; i < count; i++)
{
const short* v = (short*)pInputStream;
pVertexRegister->x = v[0];
pVertexRegister->y = v[1];
pVertexRegister->z = v[2];
pVertexRegister->w = v[3];
pInputStream = (BYTE*)pInputStream + stride;
pVertexRegister++;
}
}
//-----------------------------------------------------------------------------
// Based on register and data type the function computes FVF dword and presence
// bits:
// - Bits in the dwFVF2 are used to detect that some field is not entered twice
// - pnFloats is used to compute number of floats with position
//
// Bits for dwFVF2. Order is the same as in the FVF!!!
//
static const DWORD __POSITION_PRESENT = 1 << 0;
static const DWORD __BLENDWEIGHT_PRESENT = 1 << 1;
static const DWORD __BLENDINDICES_PRESENT = 1 << 2;
static const DWORD __NORMAL_PRESENT = 1 << 3;
static const DWORD __PSIZE_PRESENT = 1 << 4;
static const DWORD __DIFFUSE_PRESENT = 1 << 5;
static const DWORD __SPECULAR_PRESENT = 1 << 6;
// __TEXTURE0_PRESENT must start from 8th bit
static const DWORD __TEXTURE0_PRESENT = 1 << 8;
static const DWORD __TEXTURE1_PRESENT = 1 << 9;
static const DWORD __TEXTURE2_PRESENT = 1 << 10;
static const DWORD __TEXTURE3_PRESENT = 1 << 11;
static const DWORD __TEXTURE4_PRESENT = 1 << 12;
static const DWORD __TEXTURE5_PRESENT = 1 << 13;
static const DWORD __TEXTURE6_PRESENT = 1 << 14;
static const DWORD __TEXTURE7_PRESENT = 1 << 15;
static const DWORD __POSITION2_PRESENT = 1 << 16;
static const DWORD __NORMAL2_PRESENT = 1 << 17;
// Check if any bit left from the CurrentBit is set in PresenceBits
// PresenceBits are updated by CurrentBit.
//
inline void CheckOrder(
DWORD* pPresenceBits, // Presence bits for the declaration
DWORD CurrentBit,
BOOL* pFlag, // Out of order flag for the declaration
char* s) // Name of the field
{
if (*pPresenceBits & CurrentBit)
{
char msg[80];
sprintf(msg, "%s specified twice in the declaration", s);
D3D_THROW_FAIL(msg);
}
if (*pPresenceBits & ~(CurrentBit | (CurrentBit-1)))
{
*pFlag = FALSE;
}
*pPresenceBits |= CurrentBit;
}
void UpdateFVF(DWORD dwRegister, DWORD dwDataType,
DWORD* pdwFVF, // FVF for the current declaration
DWORD* pdwFVF2, // Presence bits for the current stream
DWORD* pnFloats,
BOOL* pbLegacyFVF)
{
switch (dwRegister)
{
case D3DVSDE_POSITION:
if (dwDataType != D3DVSDT_FLOAT3)
D3D_THROW_FAIL("Position register must be FLOAT3 for fixed-function pipeline");
CheckOrder(pdwFVF2, __POSITION_PRESENT, pbLegacyFVF, "Position");
*pdwFVF |= D3DFVF_XYZ;
break;
case D3DVSDE_POSITION2:
if (dwDataType != D3DVSDT_FLOAT3)
D3D_THROW_FAIL("Position2 register must be FLOAT3 for fixed-function pipeline");
CheckOrder(pdwFVF2, __POSITION2_PRESENT, pbLegacyFVF, "Position2");
break;
case D3DVSDE_BLENDWEIGHT:
{
CheckOrder(pdwFVF2, __BLENDWEIGHT_PRESENT, pbLegacyFVF, "Blend weight");
switch (dwDataType)
{
case D3DVSDT_FLOAT1:
(*pnFloats)++;
break;
case D3DVSDT_FLOAT2:
(*pnFloats) += 2;
break;
case D3DVSDT_FLOAT3:
(*pnFloats) += 3;
break;
case D3DVSDT_FLOAT4:
(*pnFloats) += 4;
break;
default:
D3D_THROW_FAIL("Invalid data type set for vertex blends");
break;
}
break;
}
case D3DVSDE_NORMAL:
CheckOrder(pdwFVF2, __NORMAL_PRESENT, pbLegacyFVF, "Normal");
if (dwDataType != D3DVSDT_FLOAT3)
D3D_THROW_FAIL("Normal register must be FLOAT3 for fixed-function pipeline");
*pdwFVF |= D3DFVF_NORMAL;
break;
case D3DVSDE_NORMAL2:
CheckOrder(pdwFVF2, __NORMAL2_PRESENT, pbLegacyFVF, "Normal2");
if (dwDataType != D3DVSDT_FLOAT3)
D3D_THROW_FAIL("Normal2 register must be FLOAT3 for fixed-function pipeline");
break;
case D3DVSDE_PSIZE:
CheckOrder(pdwFVF2, __PSIZE_PRESENT, pbLegacyFVF, "Point size");
if (dwDataType != D3DVSDT_FLOAT1)
D3D_THROW_FAIL("Point size register must be FLOAT1 for fixed-function pipeline");
*pdwFVF |= D3DFVF_PSIZE;
break;
case D3DVSDE_DIFFUSE:
CheckOrder(pdwFVF2, __DIFFUSE_PRESENT, pbLegacyFVF, "Diffuse");
if (dwDataType != D3DVSDT_D3DCOLOR)
D3D_THROW_FAIL("Diffuse register must be D3DCOLOR for fixed-function pipeline");
*pdwFVF |= D3DFVF_DIFFUSE;
break;
case D3DVSDE_SPECULAR:
CheckOrder(pdwFVF2, __SPECULAR_PRESENT, pbLegacyFVF, "Specular");
if (dwDataType != D3DVSDT_D3DCOLOR)
D3D_THROW_FAIL("Specular register must be D3DCOLOR for fixed-function pipeline");
*pdwFVF |= D3DFVF_SPECULAR;
break;
case D3DVSDE_BLENDINDICES:
CheckOrder(pdwFVF2, __BLENDINDICES_PRESENT, pbLegacyFVF, "Blend indices");
if (dwDataType != D3DVSDT_UBYTE4)
D3D_THROW_FAIL("Blend indices register must be D3DVSDT_UBYTE4 for fixed-function pipeline");
// Update number of floats after position
(*pnFloats)++;
break;
case D3DVSDE_TEXCOORD0:
case D3DVSDE_TEXCOORD1:
case D3DVSDE_TEXCOORD2:
case D3DVSDE_TEXCOORD3:
case D3DVSDE_TEXCOORD4:
case D3DVSDE_TEXCOORD5:
case D3DVSDE_TEXCOORD6:
case D3DVSDE_TEXCOORD7:
{
DWORD dwTextureIndex = dwRegister - D3DVSDE_TEXCOORD0;
DWORD dwBit = __TEXTURE0_PRESENT << dwTextureIndex;
CheckOrder(pdwFVF2, dwBit, pbLegacyFVF, "Texture");
switch (dwDataType)
{
case D3DVSDT_FLOAT1:
*pdwFVF |= D3DFVF_TEXCOORDSIZE1(dwTextureIndex);
break;
case D3DVSDT_FLOAT2:
*pdwFVF |= D3DFVF_TEXCOORDSIZE2(dwTextureIndex);
break;
case D3DVSDT_FLOAT3:
*pdwFVF |= D3DFVF_TEXCOORDSIZE3(dwTextureIndex);
break;
case D3DVSDT_FLOAT4:
*pdwFVF |= D3DFVF_TEXCOORDSIZE4(dwTextureIndex);
break;
default:
D3D_THROW_FAIL("Invalid data type set for texture register");
break;
}
break;
}
default:
D3D_THROW_FAIL("Invalid register set for fixed-function pipeline");
break;
}
}
//-----------------------------------------------------------------------------
void CVStreamDecl::Parse(CD3DBase* pDevice,
DWORD CONST ** ppToken, BOOL bFixedFunction,
DWORD* pdwFVF, DWORD* pdwFVF2, DWORD* pnFloats,
BOOL* pbLegacyFVF, UINT usage, BOOL bTessStream)
{
CONST DWORD* pToken = *ppToken;
// Used to compute stream stride and offset in bytes for each stream element
DWORD dwCurrentOffset = 0;
// FVF and FVF2 for this stream only. Used to check if data in the stream
// form a FVF subset
DWORD dwFVF2 = 0;
DWORD dwFVF = 0;
DWORD nFloats = 0;
// Set to TRUE, if data in the stream is an FVF subset
BOOL bFVFSubset = TRUE;
while (TRUE)
{
DWORD dwToken = *pToken++;
const DWORD dwTokenType = D3DVSD_GETTOKENTYPE(dwToken);
switch (dwTokenType)
{
case D3DVSD_TOKEN_NOP: break;
case D3DVSD_TOKEN_TESSELLATOR:
{
*pbLegacyFVF = FALSE;
bFVFSubset = FALSE;
const DWORD dwDataType = D3DVSD_GETDATATYPE(dwToken);
switch (dwDataType)
{
case D3DVSDT_FLOAT2:
case D3DVSDT_FLOAT3:
break;
}
break;
}
case D3DVSD_TOKEN_STREAMDATA:
{
switch (D3DVSD_GETDATALOADTYPE(dwToken))
{
case D3DVSD_LOADREGISTER:
{
#if DBG
if (m_dwNumElements >= __NUMELEMENTS)
{
D3D_ERR("D3DVSD_TOKEN_STREAMDATA:");
D3D_ERR(" Number of vertex elements in a stream is greater than max supported");
D3D_ERR(" Max supported number of elements is %d", __NUMELEMENTS);
D3D_THROW_FAIL("");
}
#endif
CVElement* pElement = &m_Elements[m_dwNumElements++];
const DWORD dwDataType = D3DVSD_GETDATATYPE(dwToken);
const DWORD dwRegister = D3DVSD_GETVERTEXREG(dwToken);
pElement->m_dwOffset = dwCurrentOffset;
pElement->m_dwRegister = dwRegister;
pElement->m_dwDataType = dwDataType;
switch (dwDataType)
{
case D3DVSDT_FLOAT1:
dwCurrentOffset += sizeof(float);
pElement->m_pfnCopy = (LPVOID)Copy_FLOAT1;
break;
case D3DVSDT_FLOAT2:
dwCurrentOffset += sizeof(float) * 2;
pElement->m_pfnCopy = (LPVOID)Copy_FLOAT2;
break;
case D3DVSDT_FLOAT3:
dwCurrentOffset += sizeof(float) * 3;
pElement->m_pfnCopy = (LPVOID)Copy_FLOAT3;
break;
case D3DVSDT_FLOAT4:
dwCurrentOffset += sizeof(float) * 4;
pElement->m_pfnCopy = (LPVOID)Copy_FLOAT4;
break;
case D3DVSDT_D3DCOLOR:
dwCurrentOffset += sizeof(DWORD);
pElement->m_pfnCopy = (LPVOID)Copy_D3DCOLOR;
break;
case D3DVSDT_UBYTE4:
#if DBG
// Do not fail when software processing will be used
if (pDevice->GetD3DCaps()->VertexProcessingCaps & D3DVTXPCAPS_NO_VSDT_UBYTE4 &&
!((usage & D3DUSAGE_SOFTWAREPROCESSING &&
pDevice->BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING) ||
(pDevice->BehaviorFlags() & D3DCREATE_SOFTWARE_VERTEXPROCESSING)))
{
D3D_THROW_FAIL("Device does not support UBYTE4 data type");
}
#endif // DBG
dwCurrentOffset += sizeof(DWORD);
pElement->m_pfnCopy = (LPVOID)Copy_UBYTE4;
break;
case D3DVSDT_SHORT2:
dwCurrentOffset += sizeof(short) * 2;
pElement->m_pfnCopy = (LPVOID)Copy_SHORT2;
break;
case D3DVSDT_SHORT4:
dwCurrentOffset += sizeof(short) * 4;
pElement->m_pfnCopy = (LPVOID)Copy_SHORT4;
break;
default:
D3D_ERR("D3DVSD_TOKEN_STREAMDATA: Invalid element data type: %10x", dwToken);
D3D_THROW_FAIL("");
}
// Compute input FVF for fixed-function pipeline
if (bFixedFunction)
{
// Update FVF for the declaration
UpdateFVF(dwRegister, dwDataType, pdwFVF, pdwFVF2,
pnFloats, pbLegacyFVF);
// Update FVF for the stream
UpdateFVF(dwRegister, dwDataType, &dwFVF, &dwFVF2,
&nFloats, &bFVFSubset);
}
else
if (dwRegister >= D3DVS_INPUTREG_MAX_V1_1)
D3D_THROW_FAIL("D3DVSD_TOKEN_STREAMDATA: Invalid register number");
break;
}
case D3DVSD_SKIP:
{
if (bFixedFunction)
{
D3D_THROW_FAIL("D3DVSD_SKIP is not allowed for fixed-function pipeline");
}
const DWORD dwCount = D3DVSD_GETSKIPCOUNT(dwToken);
dwCurrentOffset += dwCount * sizeof(DWORD);
break;
}
default:
D3D_ERR("Invalid data load type: %10x", dwToken);
D3D_THROW_FAIL("");
}
break;
}
default:
{
*ppToken = pToken - 1;
m_dwStride = dwCurrentOffset;
goto l_exit;
}
} // switch
} // while
l_exit:
if (bFixedFunction && !bTessStream)
{
#if DBG
m_dwFVF = dwFVF;
#endif
if (!bFVFSubset)
{
D3D_THROW_FAIL("For fixed-function pipeline each stream has to be an FVF subset");
}
if (dwFVF2 & (__POSITION2_PRESENT | __NORMAL2_PRESENT |
__PSIZE_PRESENT | __BLENDINDICES_PRESENT))
{
*pbLegacyFVF = FALSE;
}
}
}
//-----------------------------------------------------------------------------
CVDeclaration::CVDeclaration(DWORD dwNumStreams)
{
m_pConstants = NULL;
m_pConstantsTail = NULL;
m_pActiveStreams = NULL;
m_pActiveStreamsTail = NULL;
m_dwInputFVF = 0;
m_bLegacyFVF = TRUE;
m_dwNumStreams = dwNumStreams;
m_bStreamTessPresent = FALSE;
}
//-----------------------------------------------------------------------------
CVDeclaration::~CVDeclaration()
{
delete m_pActiveStreams;
delete m_pConstants;
}
//-----------------------------------------------------------------------------
void CVDeclaration::Parse(CD3DBase* pDevice, CONST DWORD* pTok, BOOL bFixedFunction,
DWORD* pDeclSize, UINT usage)
{
DWORD dwFVF = 0; // FVF for fixed-function pipeline
DWORD dwFVF2 = 0; // Texture presence bits (8 bits)
DWORD nFloats = 0; // Number of floats after position
DWORD dwStreamPresent = 0; // Bit is set if a stream is used
m_bLegacyFVF = TRUE;
CONST DWORD* pToken = pTok;
while (TRUE)
{
DWORD dwToken = *pToken++;
const DWORD dwTokenType = D3DVSD_GETTOKENTYPE(dwToken);
switch (dwTokenType)
{
case D3DVSD_TOKEN_NOP: break;
case D3DVSD_TOKEN_STREAM:
{
CVStreamDecl StreamTess;
if( D3DVSD_ISSTREAMTESS(dwToken) )
{
m_bLegacyFVF = FALSE;
if( m_bStreamTessPresent )
{
D3D_THROW(D3DERR_INVALIDCALL, "Tesselator Stream has already been defined in the declaration");
}
m_bStreamTessPresent = TRUE;
//
// For now simply skip over the Tess tokens in the
// Runtime.
StreamTess.Parse(pDevice, &pToken, bFixedFunction, &dwFVF, &dwFVF2,
&nFloats, &m_bLegacyFVF, usage, TRUE);
}
else
{
DWORD dwStream = D3DVSD_GETSTREAMNUMBER(dwToken);
if (dwStream >= m_dwNumStreams)
{
D3D_THROW_FAIL("Stream number is too big");
}
if (dwStreamPresent & (1 << dwStream))
{
D3D_THROW(D3DERR_INVALIDCALL, "Stream is already defined"
"in the declaration");
}
dwStreamPresent |= 1 << dwStream;
// There are more than one stream present, so cant be
// handled by legacy FVF.
if( dwStreamPresent & (dwStreamPresent - 1) )
m_bLegacyFVF = FALSE;
CVStreamDecl* pStream = new CVStreamDecl;
if (pStream == NULL)
{
D3D_THROW(E_OUTOFMEMORY, "Not enough memory");
}
try
{
pStream->Parse(pDevice, &pToken, bFixedFunction, &dwFVF, &dwFVF2,
&nFloats, &m_bLegacyFVF, usage);
pStream->m_dwStreamIndex = dwStream;
if (m_pActiveStreams == NULL)
{
m_pActiveStreams = pStream;
m_pActiveStreamsTail = pStream;
}
else
{
m_pActiveStreamsTail->Append(pStream);
m_pActiveStreamsTail = pStream;
}
}
catch (HRESULT e)
{
delete pStream;
throw e;
}
}
break;
}
case D3DVSD_TOKEN_STREAMDATA:
{
D3D_THROW_FAIL("D3DVSD_TOKEN_STREAMDATA could only be used after D3DVSD_TOKEN_STREAM");
}
case D3DVSD_TOKEN_CONSTMEM:
{
CVConstantData * cd = new CVConstantData;
CheckForNull(cd, __LINE__, __FILE__);
cd->m_dwCount = D3DVSD_GETCONSTCOUNT(dwToken);
cd->m_dwAddress = D3DVSD_GETCONSTADDRESS(dwToken);
UINT ValidationCount;
if (usage & D3DUSAGE_SOFTWAREPROCESSING)
ValidationCount = D3DVS_CONSTREG_MAX_V1_1;
else
ValidationCount = pDevice->GetD3DCaps()->MaxVertexShaderConst;
if ((cd->m_dwCount + cd->m_dwAddress) > ValidationCount)
D3D_THROW_FAIL("D3DVSD_TOKEN_CONSTMEM writes outside constant memory");
const DWORD dwSize = cd->m_dwCount << 2; // number of DWORDs
cd->m_pData = new DWORD[dwSize];
CheckForNull(cd->m_pData, __LINE__, __FILE__);
memcpy(cd->m_pData, pToken, dwSize << 2);
if (m_pConstants == NULL)
{
m_pConstants = cd;
m_pConstantsTail = cd;
}
else
{
m_pConstantsTail->Append(cd);
m_pConstantsTail = cd;
}
pToken += dwSize;
break;
}
case D3DVSD_TOKEN_EXT:
{
// Skip extension info
DWORD dwCount = D3DVSD_GETEXTCOUNT(dwToken);
pToken += dwCount;
break;
}
case D3DVSD_TOKEN_END:
{
goto l_End;
}
default:
{
D3D_ERR("Invalid declaration token: %10x", dwToken);
D3D_THROW_FAIL("");
}
}
}
l_End:
// Validate input for the fixed-function pipeline
if (bFixedFunction && !m_bStreamTessPresent)
{
m_dwInputFVF = dwFVF & 0xFFFF0FFF; // Remove float count
switch (nFloats)
{
case 0: m_dwInputFVF |= D3DFVF_XYZ; break;
case 1: m_dwInputFVF |= D3DFVF_XYZB1; break;
case 2: m_dwInputFVF |= D3DFVF_XYZB2; break;
case 3: m_dwInputFVF |= D3DFVF_XYZB3; break;
case 4: m_dwInputFVF |= D3DFVF_XYZB4; break;
case 5: m_dwInputFVF |= D3DFVF_XYZB5; break;
default:
D3D_THROW_FAIL("Too many floats after position");
}
// Compute number of texture coordinates
DWORD nTexCoord = 0;
DWORD dwTexturePresenceBits = (dwFVF2 >> 8) & 0xFF;
while (dwTexturePresenceBits & 1)
{
dwTexturePresenceBits >>= 1;
nTexCoord++;
}
// There should be no gaps in texture coordinates
if (dwTexturePresenceBits)
D3D_THROW_FAIL("Texture coordinates should have no gaps");
m_dwInputFVF |= nTexCoord << D3DFVF_TEXCOUNT_SHIFT;
// Position must be set
if ((dwFVF & D3DFVF_POSITION_MASK) != D3DFVF_XYZ)
D3D_THROW_FAIL("Position register must be set");
}
if (pDeclSize != NULL)
{
*pDeclSize = (DWORD) (pToken - pTok) << 2;
}
}
//---------------------------------------------------------------------
CVStream::~CVStream()
{
if (m_pVB)
m_pVB->DecrementUseCount();
}
//---------------------------------------------------------------------
CVIndexStream::~CVIndexStream()
{
if (m_pVBI)
m_pVBI->DecrementUseCount();
}
//---------------------------------------------------------------------
DWORD g_PrimToVerCount[7][2] =
{
{0, 0}, // Illegal
{1, 0}, // D3DPT_POINTLIST = 1,
{2, 0}, // D3DPT_LINELIST = 2,
{1, 1}, // D3DPT_LINESTRIP = 3,
{3, 0}, // D3DPT_TRIANGLELIST = 4,
{1, 2}, // D3DPT_TRIANGLESTRIP = 5,
{1, 2}, // D3DPT_TRIANGLEFAN = 6,
};
//-----------------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::CreateShader(CVElement* pElements, DWORD dwNumElements,
DWORD* pdwShaderCode, DWORD dwOutputFVF,
CPSGPShader** ppPSGPShader)
{
*ppPSGPShader = NULL;
try
{
*ppPSGPShader = m_VertexVM.CreateShader(pElements, dwNumElements,
pdwShaderCode);
if (*ppPSGPShader == NULL)
return D3DERR_INVALIDCALL;
}
D3D_CATCH;
return D3D_OK;
}
//-----------------------------------------------------------------------------
HRESULT D3DFE_PVFUNCSI::SetActiveShader(CPSGPShader* pPSGPShader)
{
return m_VertexVM.SetActiveShader((CVShaderCode*)pPSGPShader);
}
//-----------------------------------------------------------------------------
// Load vertex shader constants
HRESULT
D3DFE_PVFUNCSI::LoadShaderConstants(DWORD start, DWORD count, LPVOID buffer)
{
return m_VertexVM.SetData(D3DSPR_CONST, start, count, buffer);
}
//-----------------------------------------------------------------------------
// Get vertex shader constants
HRESULT
D3DFE_PVFUNCSI::GetShaderConstants(DWORD start, DWORD count, LPVOID buffer)
{
return m_VertexVM.GetData(D3DSPR_CONST, start, count, buffer);
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::GetVertexShaderConstant"
HRESULT D3DAPI
CD3DHal::GetVertexShaderConstant(DWORD Register, LPVOID pData, DWORD count)
{
API_ENTER(this);
#if DBG
// Validate Parameters
if (!VALID_WRITEPTR(pData, 4 * sizeof(DWORD) * count))
{
D3D_ERR("Invalid constant data pointer. GetVertexShaderConstant failed.");
return D3DERR_INVALIDCALL;
}
if ((GetD3DCaps()->VertexShaderVersion == D3DVS_VERSION(0,0)) &&
(BehaviorFlags() & (D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE)))
{
D3D_ERR("No programmable vertex shaders are supported by this device. GetVertexShaderConstant failed.");
return D3DERR_INVALIDCALL;
}
UINT ValidationCount;
if (BehaviorFlags() & D3DCREATE_MIXED_VERTEXPROCESSING)
ValidationCount = max(m_MaxVertexShaderConst, D3DVS_CONSTREG_MAX_V1_1);
else
if (BehaviorFlags() & D3DCREATE_SOFTWARE_VERTEXPROCESSING)
ValidationCount = D3DVS_CONSTREG_MAX_V1_1;
else
ValidationCount = m_MaxVertexShaderConst;
if((Register + count) > ValidationCount)
{
D3D_ERR("Not that many constant registers in the vertex machine. GetVertexShaderConstant failed.");
return D3DERR_INVALIDCALL;
}
#endif
HRESULT hr;
if (m_dwRuntimeFlags & D3DRT_RSSOFTWAREPROCESSING ||
((count + Register) <= D3DVS_CONSTREG_MAX_V1_1))
{
// For software vertex processing we store constant registers in PSGP if
// possible
return m_pv->pGeometryFuncs->GetShaderConstants(Register, count,
const_cast<VOID*>(pData));
}
else
{
if (Register >= D3DVS_CONSTREG_MAX_V1_1)
{
// When all modified registers are above software limit, we use Microsoft
// internal array
hr = GeometryFuncsGuaranteed->GetShaderConstants(Register, count,
const_cast<VOID*>(pData));
}
else
{
// Part of constant data is taken from PSGP array and part from
// Microsoft's array
UINT FirstCount = D3DVS_CONSTREG_MAX_V1_1 - Register;
hr = m_pv->pGeometryFuncs->GetShaderConstants(Register, FirstCount,
const_cast<VOID*>(pData));
if (FAILED(hr))
{
return hr;
}
return GeometryFuncsGuaranteed->GetShaderConstants(D3DVS_CONSTREG_MAX_V1_1,
Register + count - D3DVS_CONSTREG_MAX_V1_1,
&((DWORD*)pData)[FirstCount*4]);
}
return hr;
}
return m_pv->pGeometryFuncs->GetShaderConstants(Register, count, pData);
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::GetVertexShader"
HRESULT D3DAPI
CD3DHal::GetVertexShader(LPDWORD pdwHandle)
{
API_ENTER(this); // Takes D3D Lock if necessary
HRESULT ret = D3D_OK;
#if DBG
// Validate Parameters
if (!VALID_WRITEPTR(pdwHandle, sizeof(DWORD)))
{
D3D_ERR("Invalid handle pointer. GetVertexShader failed.");
return D3DERR_INVALIDCALL;
}
#endif
*pdwHandle = m_dwCurrentShaderHandle;
return ret;
}
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::GetPixelShader"
HRESULT D3DAPI
CD3DHal::GetPixelShader(LPDWORD pdwHandle)
{
API_ENTER(this); // Takes D3D Lock if necessary
HRESULT ret = D3D_OK;
#if DBG
// Validate Parameters
if (!VALID_WRITEPTR(pdwHandle, sizeof(DWORD)))
{
D3D_ERR("Invalid handle pointer. GetPixelShader failed.");
return D3DERR_INVALIDCALL;
}
#endif
*pdwHandle = m_dwCurrentPixelShaderHandle;
return ret;
}
#if DBG
//-----------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CD3DHal::ValidateRTPatch"
void CD3DHal::ValidateRTPatch()
{
if (D3DVSD_ISLEGACY(m_dwCurrentShaderHandle))
{
if (m_pStream[0].m_pVB == 0)
{
D3D_THROW_FAIL("Draw[RT]Patch should have streams set");
}
if ((m_pStream[0].m_pVB->GetBufferDesc()->Usage & D3DUSAGE_RTPATCHES) == 0)
{
D3D_THROW_FAIL("Vertex buffers used for rendering RT-Patches should have D3DUSAGE_RTPATCHES set");
}
}
else
{
CVStreamDecl* pStream;
pStream = m_pCurrentShader->m_Declaration.m_pActiveStreams;
while(pStream)
{
UINT index = pStream->m_dwStreamIndex;
CVStream* pDeviceStream = &m_pStream[index];
if (pDeviceStream->m_pVB == 0)
{
D3D_THROW_FAIL("Draw[RT]Patch should have streams set");
}
if ((pDeviceStream->m_pVB->GetBufferDesc()->Usage & D3DUSAGE_RTPATCHES) == 0)
{
D3D_THROW_FAIL("Vertex buffers used for rendering RT-Patches should have D3DUSAGE_RTPATCHES set");
}
pStream = (CVStreamDecl*)pStream->m_pNext;
}
}
}
#endif // DBG