/*============================================================================ * * 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((*m_pRTPatchValidationInfo)[Handle].m_pObj)->m_ShaderHandle == dwHandle) { static_cast((*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(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(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(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(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(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(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(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(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(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(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(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