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

1260 lines
40 KiB

/*==========================================================================;
*
* Copyright (C) 1995 Microsoft Corporation. All Rights Reserved.
*
* File: vertbuf.cpp
* Content: Direct3DVertexBuffer implementation
*@@BEGIN_MSINTERNAL
*
* History:
* Date By Reason
* ==== == ======
*@@END_MSINTERNAL
*
***************************************************************************/
#include "pch.cpp"
#pragma hdrstop
#include "drawprim.hpp"
#include "d3dfei.h"
const DWORD D3DVOP_RENDER = 1 << 31;
const DWORD D3DVBCAPS_VALID = D3DVBCAPS_SYSTEMMEMORY |
D3DVBCAPS_WRITEONLY |
D3DVBCAPS_OPTIMIZED;
void hookVertexBufferToD3D(LPDIRECT3DI lpDirect3DI,
LPDIRECT3DVERTEXBUFFERI lpVBufI)
{
LIST_INSERT_ROOT(&lpDirect3DI->vbufs, lpVBufI, list);
lpVBufI->lpDirect3DI = lpDirect3DI;
lpDirect3DI->numVBufs++;
}
/*
* Direct3DVertexBuffer::QueryInterface
*/
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::QueryInterface"
HRESULT D3DAPI CDirect3DVertexBuffer::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock.
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDOBJECT;
}
if (!VALID_OUTPTR(ppvObj)) {
D3D_ERR( "Invalid pointer to pointer" );
return DDERR_INVALIDPARAMS;
}
*ppvObj = NULL;
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return DDERR_INVALIDPARAMS;
}
#endif
if(IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IDirect3DVertexBuffer) )
{
AddRef();
*ppvObj = static_cast<LPVOID>(static_cast<LPDIRECT3DVERTEXBUFFER>(this));
return(D3D_OK);
}
else
{
D3D_ERR( "Don't know this riid" );
return (E_NOINTERFACE);
}
} /* CDirect3DVertexBuffer::QueryInterface */
/*
* Direct3DVertexBuffer::AddRef
*/
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::AddRef"
ULONG D3DAPI CDirect3DVertexBuffer::AddRef()
{
DWORD rcnt;
CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock.
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return 0;
}
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
#endif
this->refCnt++;
rcnt = this->refCnt;
return (rcnt);
} /* Direct3DVertexBuffer::AddRef */
/*
* Direct3DVertexBuffer::Release
*
*/
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::Release"
ULONG D3DAPI CDirect3DVertexBuffer::Release()
{
DWORD lastrefcnt;
CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock.
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return 0;
}
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
#endif
/*
* decrement the ref count. if we hit 0, free the object
*/
this->refCnt--;
lastrefcnt = this->refCnt;
if( lastrefcnt == 0 )
{
delete this;
return 0;
}
return lastrefcnt;
} /* D3DTex3_Release */
//---------------------------------------------------------------------
// Internal version.
// No D3D lock, no checks
//
#undef DPF_MODNAME
#define DPF_MODNAME "DIRECT3DI::CreateVertexBufferI"
HRESULT DIRECT3DI::CreateVertexBufferI(LPD3DVERTEXBUFFERDESC lpDesc,
LPDIRECT3DVERTEXBUFFER* lplpVBuf,
DWORD dwFlags)
{
CDirect3DVertexBuffer* lpVBufI;
HRESULT ret = D3D_OK;
*lplpVBuf = NULL;
lpVBufI = static_cast<LPDIRECT3DVERTEXBUFFERI>(new CDirect3DVertexBuffer(this));
if (!lpVBufI) {
D3D_ERR("failed to allocate space for vertex buffer");
return (DDERR_OUTOFMEMORY);
}
if ((ret=lpVBufI->Init(this, lpDesc, dwFlags))!=D3D_OK)
{
D3D_ERR("Failed to initialize the vertex buffer object");
delete lpVBufI;
return ret;
}
*lplpVBuf = (LPDIRECT3DVERTEXBUFFER)lpVBufI;
return(D3D_OK);
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "DIRECT3DI::CreateVertexBuffer"
HRESULT D3DAPI DIRECT3DI::CreateVertexBuffer(LPD3DVERTEXBUFFERDESC lpDesc, LPDIRECT3DVERTEXBUFFER* lplpVBuf,
DWORD dwFlags, LPUNKNOWN pUnkOuter)
{
if(pUnkOuter != NULL) {
D3D_ERR("Unknown pointer should be NULL");
return CLASS_E_NOAGGREGATION;
}
CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock.
#if DBG
/*
* validate parms
*/
if (!VALID_DIRECT3D3_PTR(this)) {
D3D_ERR( "Invalid Direct3D pointer" );
return DDERR_INVALIDOBJECT;
}
if (!VALID_OUTPTR(lplpVBuf)) {
D3D_ERR( "Invalid pointer to pointer pointer" );
return DDERR_INVALIDPARAMS;
}
if ((lpDesc->dwCaps & D3DVBCAPS_VALID) != lpDesc->dwCaps)
{
D3D_ERR("Invalid caps");
return DDERR_INVALIDCAPS;
}
if (dwFlags & ~D3DDP_DONOTCLIP)
{
D3D_ERR("Invalid dwFlags");
return DDERR_INVALIDPARAMS;
}
#endif
return CreateVertexBufferI(lpDesc, lplpVBuf, dwFlags);
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::constructor"
CDirect3DVertexBuffer::CDirect3DVertexBuffer(LPDIRECT3DI lpD3DI)
{
refCnt = 1;
/*
* Put this vertex buffer in the list of those owned by the
* Direct3D object
*/
hookVertexBufferToD3D(lpD3DI, this);
srcVOP = dstVOP = dwPVFlags = position.dwStride = dwLockCnt = 0;
legacyVertexType = (D3DVERTEXTYPE)0;
position.lpvData = NULL;
clipCodes = NULL;
lpDDSVB = NULL;
dwCaps = 0;
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::destructor"
CDirect3DVertexBuffer::~CDirect3DVertexBuffer()
{
/*
* Remove ourselves from the Direct3D object
*/
LIST_DELETE(this, list);
this->lpDirect3DI->numVBufs--;
delete [] clipCodes;
if (lpDDSVB)
{
lpDDSVB->Release();
lpDDS1VB->Release();
}
}
/* Calculates the per vertex size in bytes based on the vertex ID
* This function ignores the CLIPFLAGS field since it is allocated
* separatly.
*/
DWORD calcVertexSize(DWORD fvf)
{
DWORD vertSize=0;
static const BYTE nibble1[]={0, 0,
12, 12,
16, 16,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static const BYTE nibble2[]={0, 12, 4, 16, 4, 16, 8, 20, 4, 16, 8, 20, 8, 20, 12, 24};
#if DBG
if (fvf & D3DFVF_XYZ)
vertSize += 12;
else if (fvf & D3DFVF_XYZRHW)
vertSize += 16;
if (fvf & D3DFVF_NORMAL)
vertSize += 12;
if (fvf & D3DFVF_RESERVED1)
vertSize += 4;
if (fvf & D3DFVF_DIFFUSE)
vertSize += 4;
if (fvf & D3DFVF_SPECULAR)
vertSize += 4;
#else
vertSize = nibble1[fvf&0xf] + nibble2[(fvf>>4)&0xf];
#endif
vertSize += ((fvf >> 8) & 0xf) << 3; // 8 * #textures
return vertSize;
}
//---------------------------------------------------------------------
//
// Create the vertex memory buffer through DirectDraw
//
// Notes:
// this->dwMemType should be set before calling this function
// this->dwCaps should be set too.
// this->dwMemType is set to DDSCAPS_VIDEOMEMORY is the VB was driver allocated
//
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::CreateMemoryBuffer"
HRESULT CDirect3DVertexBuffer::CreateMemoryBuffer(
LPDIRECT3DI lpD3DI,
LPDIRECTDRAWSURFACE4 *lplpSurface4,
LPDIRECTDRAWSURFACE *lplpSurface,
LPVOID *lplpMemory,
DWORD dwBufferSize,
DWORD dwFlags)
{
HRESULT ret;
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ddsd.dwFlags = DDSD_WIDTH | DDSD_CAPS;
ddsd.dwWidth = dwBufferSize;
ddsd.ddsCaps.dwCaps = DDSCAPS_EXECUTEBUFFER;
ddsd.ddsCaps.dwCaps2 = this->dwMemType;
// The meaning of DDSCAPS_VIDEOMEMORY and DDSCAPS_SYSTEMEMORY are
// slightly different in case of VBs. the former only means that
// the buffer is drivers allocated and could be in any memory type.
// The latter means that the driver did not care to allocate VBs
// hence they are always in DDraw allocated system memory.
// The reason we try video memory followed by system memory
// (rather than simply not specifying the memory type) is for
// drivers which do not care to do any special VB allocations, we
// do not DDraw to take the Win16 lock for locking system memory
// surfaces.
if ((dwCaps & D3DVBCAPS_SYSTEMMEMORY) || !FVF_TRANSFORMED(fvf))
{
// This VB cannot reside in driver friendly memory for one of the following reasons:
// 1. The app explicitly specified system memory
// 2. The vertex buffer is untransformed - the driver will never see this VB
D3D_INFO(8, "Trying to create a sys mem vertex buffer");
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
ret = lpD3DI->lpDD4->CreateSurface(&ddsd, lplpSurface4, NULL);
if (ret != DD_OK)
{
D3D_ERR("Could not allocate the Vertex buffer.");
return ret;
}
}
else
{
// Try explicit video memory first
ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
if (dwFlags & D3DDP_DONOTCLIP)
ddsd.ddsCaps.dwCaps |= dwCaps & DDSCAPS_WRITEONLY;
D3D_INFO(8, "Trying to create a vid mem vertex buffer");
if (lpD3DI->lpDD4->CreateSurface(&ddsd, lplpSurface4, NULL) != DD_OK)
{
// If that failed, or user requested sys mem, try explicit system memory
D3D_INFO(6, "Trying to create a sys mem vertex buffer");
ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_VIDEOMEMORY | DDSCAPS_WRITEONLY);
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
ret = lpD3DI->lpDD4->CreateSurface(&ddsd, lplpSurface4, NULL);
if (ret != DD_OK)
{
D3D_ERR("Could not allocate the Vertex buffer.");
return ret;
}
}
else
{
// Mark VB as REALLY in vid mem for Lock / Unlock optimizations
this->dwMemType = DDSCAPS_VIDEOMEMORY;
}
}
*lplpMemory = NULL;
if (!(this->dwMemType & DDSCAPS_VIDEOMEMORY))
{
ret = (*lplpSurface4)->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
if (ret != DD_OK)
{
D3D_ERR("Could not lock system memory Vertex Buffer.");
return ret;
}
*lplpMemory = ddsd.lpSurface;
}
ret = lpDDSVB->QueryInterface(IID_IDirectDrawSurface, (LPVOID*)lplpSurface);
if (ret != DD_OK)
{
D3D_ERR("failed to QI for DDS1");
return ret;
}
return D3D_OK;
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::init"
HRESULT CDirect3DVertexBuffer::Init(LPDIRECT3DI lpD3DI, LPD3DVERTEXBUFFERDESC lpDesc, DWORD dwFlags)
{
HRESULT ret;
bReallyOptimized = FALSE;
dwCaps = lpDesc->dwCaps;
fvf = lpDesc->dwFVF;
dwNumVertices = lpDesc->dwNumVertices;
if (dwNumVertices > MAX_DX6_VERTICES)
{
D3D_ERR("Direct3D for DirectX 6.0 cannot handle greater than 64K vertices");
return D3DERR_TOOMANYVERTICES;
}
if (lpDesc->dwCaps & D3DVBCAPS_OPTIMIZED)
{
D3D_ERR("D3DVBCAPS_OPTIMIZED flag should not be set");
return DDERR_INVALIDPARAMS;
}
if (fvf & 0xfffff000) // Higher order 20 bits must be zero for DX6
{
D3D_ERR("Invalid FVF id");
return D3DERR_INVALIDVERTEXFORMAT;
}
nTexCoord = FVF_TEXCOORD_NUMBER(fvf);
if ((position.dwStride = calcVertexSize(fvf)) == 0)
{
D3D_ERR("Vertex size is zero according to the FVF id");
return D3DERR_INVALIDVERTEXFORMAT;
}
if (dwFlags & D3DVBFLAGS_CREATEMULTIBUFFER)
dwMemType = 0;
else
dwMemType = DDSCAPS2_VERTEXBUFFER;
ret = CreateMemoryBuffer(lpD3DI, &lpDDSVB, &lpDDS1VB, &position.lpvData,
position.dwStride * dwNumVertices, dwFlags);
if (ret != D3D_OK)
return ret;
/* Classify the operations that can be done using this VB */
if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZ)
{
D3D_INFO(4, "D3DFVF_XYZ set. Can be source VB for Transform");
srcVOP = D3DVOP_TRANSFORM | D3DVOP_EXTENTS | D3DVOP_CLIP;
}
else if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
{
D3D_INFO(4, "D3DFVF_XYZRHW set. Can be dest VB for Transform");
dstVOP = D3DVOP_TRANSFORM | D3DVOP_EXTENTS;
srcVOP |= D3DVOP_EXTENTS;
if ((dwFlags & D3DDP_DONOTCLIP) == 0)
{
clipCodes = new D3DFE_CLIPCODE[dwNumVertices];
if (clipCodes == NULL)
{
D3D_ERR("Could not allocate space for clip flags");
return DDERR_OUTOFMEMORY;
}
memset(clipCodes, 0, dwNumVertices * sizeof(D3DFE_CLIPCODE));
dstVOP |= D3DVOP_CLIP;
}
}
if (fvf & D3DFVF_NORMAL)
{
D3D_INFO(4, "D3DFVF_NORMAL set.");
if (srcVOP & D3DVOP_TRANSFORM)
{
D3D_INFO(4, "Can be src VB for lighting.");
srcVOP |= D3DVOP_LIGHT;
}
this->dwPVFlags |= D3DPV_LIGHTING;
}
if (fvf & D3DFVF_DIFFUSE)
{
D3D_INFO(4, "D3DFVF_DIFFUSE set. Can be dest VB for lighting");
dstVOP |= D3DVOP_LIGHT;
}
if (dstVOP & D3DVOP_TRANSFORM)
{
D3D_INFO(4, "VB can be rendered");
srcVOP |= D3DVOP_RENDER;
}
/* Compare with legacy vertex types */
if (fvf == D3DFVF_VERTEX)
{
legacyVertexType = D3DVT_VERTEX;
}
else if (fvf == D3DFVF_LVERTEX)
{
legacyVertexType = D3DVT_LVERTEX;
}
else if (fvf == D3DFVF_TLVERTEX)
{
legacyVertexType = D3DVT_TLVERTEX;
}
return(D3D_OK);
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::Lock"
HRESULT D3DAPI CDirect3DVertexBuffer::Lock(DWORD dwFlags, LPVOID* lplpData, DWORD* lpdwSize)
{
CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock.
HRESULT ret;
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
if (IsBadWritePtr( lplpData, sizeof(LPVOID))) {
D3D_ERR( "Invalid lpData pointer" );
return DDERR_INVALIDPARAMS;
}
if (lpdwSize)
if (IsBadWritePtr( lpdwSize, sizeof(DWORD))) {
D3D_ERR( "Invalid lpData pointer" );
return DDERR_INVALIDPARAMS;
}
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
#endif
if (this->dwCaps & D3DVBCAPS_OPTIMIZED)
{
D3D_ERR("Cannot lock optimized vertex buffer");
return(D3DERR_VERTEXBUFFEROPTIMIZED);
}
return this->LockI(dwFlags, lplpData, lpdwSize);
}
//---------------------------------------------------------------------
// Side effect:
// position.lpvData is set.
//
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::LockI"
HRESULT D3DAPI CDirect3DVertexBuffer::LockI(DWORD dwFlags, LPVOID* lplpData,
DWORD* lpdwSize)
{
HRESULT ret;
if (lpdwSize)
*lpdwSize = position.dwStride * dwNumVertices;
if (position.lpvData)
{
*lplpData = position.lpvData;
}
else
{
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ret = lpDDSVB->Lock(NULL, &ddsd, dwFlags | DDLOCK_NOSYSLOCK, NULL);
if (ret != DD_OK)
{
D3D_ERR("Could not lock vertex buffer.");
return ret;
}
*lplpData = ddsd.lpSurface;
position.lpvData = ddsd.lpSurface;
}
dwLockCnt++;
D3D_INFO(6, "VB Lock: %lx Lock Cnt =%d", this, dwLockCnt);
if (lpDevIBatched && !(dwFlags & DDLOCK_READONLY))
{
ret = lpDevIBatched->FlushStates();
lpDevIBatched = NULL;
if (ret != D3D_OK)
{
D3D_ERR("Could not flush batch referring to VB during Lock");
return ret;
}
}
return D3D_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::Unlock"
HRESULT D3DAPI CDirect3DVertexBuffer::Unlock()
{
if (dwLockCnt)
{
dwLockCnt--;
if ((dwMemType & DDSCAPS_VIDEOMEMORY) && (dwLockCnt == 0))
{
position.lpvData = NULL;
D3D_INFO(6, "VB Unlock: %lx Lock Cnt =%d", this, dwLockCnt);
return lpDDSVB->Unlock(NULL);
}
}
D3D_INFO(6, "VB Unlock: %lx Lock Cnt =%d", this, dwLockCnt);
return D3D_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::GetVertexBufferDesc"
HRESULT D3DAPI CDirect3DVertexBuffer::GetVertexBufferDesc(LPD3DVERTEXBUFFERDESC lpDesc)
{
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
if (IsBadWritePtr( lpDesc, lpDesc->dwSize)) {
D3D_ERR( "Invalid lpData pointer" );
return DDERR_INVALIDPARAMS;
}
if (! VALID_D3DVERTEXBUFFERDESC_PTR(lpDesc) )
{
D3D_ERR( "Invalid D3DVERTEXBUFFERDESC" );
return DDERR_INVALIDPARAMS;
}
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
#endif
lpDesc->dwCaps = dwCaps;
lpDesc->dwFVF = fvf;
lpDesc->dwNumVertices = this->dwNumVertices;
return D3D_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DVertexBuffer::ProcessVertices"
HRESULT D3DAPI CDirect3DVertexBuffer::ProcessVertices(DWORD vertexOP, DWORD dwDstIndex, DWORD dwCount,
LPDIRECT3DVERTEXBUFFER lpSrc,
DWORD dwSrcIndex,
LPDIRECT3DDEVICE3 lpDevice, DWORD dwFlags)
{
LPDIRECT3DVERTEXBUFFERI lpSrcI;
LPDIRECT3DDEVICEI lpDevI;
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(lpSrc)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
lpSrcI = static_cast<LPDIRECT3DVERTEXBUFFERI>(lpSrc);
if (!VALID_DIRECT3DDEVICE3_PTR(lpDevice)) {
D3D_ERR( "Invalid Direct3DDevice pointer" );
return DDERR_INVALIDOBJECT;
}
lpDevI = static_cast<LPDIRECT3DDEVICEI>(lpDevice);
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
if (dwFlags != 0 ||
(dwDstIndex + dwCount) > this->dwNumVertices ||
(dwSrcIndex + dwCount) > lpSrcI->dwNumVertices)
{
D3D_ERR( "Invalid parameters" );
return DDERR_INVALIDPARAMS;
}
/* Validate Src & Dst Vertex Formats */
if ((lpSrcI->srcVOP & vertexOP) != vertexOP)
{
D3D_ERR("Source VB cannot support this operation");
return D3DERR_INVALIDVERTEXFORMAT;
}
if (!(vertexOP & D3DVOP_TRANSFORM))
{
D3D_ERR("D3DVOP_TRANSFORM flag should be set");
return DDERR_INVALIDPARAMS;
}
if ((dstVOP & vertexOP) != vertexOP)
{
D3D_ERR("Destination VB cannot support this operation");
return D3DERR_INVALIDVERTEXFORMAT;
}
if (this->fvf & D3DFVF_NORMAL)
{
D3D_ERR("The destination vertex buffer cannot have normals");
return D3DERR_INVALIDVERTEXFORMAT;
}
#else
lpSrcI = static_cast<LPDIRECT3DVERTEXBUFFERI>(lpSrc);
lpDevI = static_cast<LPDIRECT3DDEVICEI>(lpDevice);
#endif
CLockD3DMT lockObject(lpDevI, DPF_MODNAME, REMIND("")); // Takes D3D lock.
// Fill the D3DFE_PROCESSVERTICES structure
// STRIDE and SOA flags
lpDevI->dwFlags = lpSrcI->dwPVFlags & D3DPV_SOA;
// LIGHTING, EXTENTS and CLIP flags (note extents and clip flags are inverted)
// Currently transform is always done
lpDevI->dwFlags |= (vertexOP ^ (D3DVOP_CLIP | D3DVOP_EXTENTS)) | D3DVOP_TRANSFORM;
HRESULT ret;
// Download viewport ??
if (lpDevI->v_id != lpDevI->lpCurrentViewport->v_id)
{
ret = downloadView(lpDevI->lpCurrentViewport);
if (ret != D3D_OK)
return ret;
}
// Num vertices
lpDevI->dwNumVertices = dwCount;
// Lock the VBs
LPVOID lpVoid;
ret = LockI(DDLOCK_WAIT, &lpVoid, NULL);
if (ret != D3D_OK)
{
D3D_ERR("Could not lock the vertex buffer");
return ret;
}
ret = lpSrcI->LockI(DDLOCK_WAIT | DDLOCK_READONLY, &lpVoid, NULL);
if (ret != D3D_OK)
{
D3D_ERR("Could not lock the vertex buffer");
return ret;
}
// Output
lpDevI->lpvOut = LPVOID(LPBYTE(position.lpvData) + dwDstIndex * position.dwStride);
lpDevI->lpClipFlags = clipCodes + dwDstIndex;
lpDevI->dwVIDIn = lpSrcI->fvf;
lpDevI->dwVIDOut = fvf;
if (lpSrcI->legacyVertexType && legacyVertexType)
{ // AOS Legacy
/* We can use the legacy FE codepaths which might
* be faster
*/
lpDevI->position.lpvData = LPVOID(LPBYTE(lpSrcI->position.lpvData) + dwSrcIndex * lpSrcI->position.dwStride);
lpDevI->nTexCoord = 1;
lpDevI->position.dwStride = lpSrcI->position.dwStride;
lpDevI->dwOutputSize = position.dwStride;
}
else
{
lpDevI->nTexCoord = min(nTexCoord, lpSrcI->nTexCoord);
if (lpSrcI->bReallyOptimized)
{ // SOA
// Assume that SOA.lpvData is the same as position.lpvData
lpDevI->SOA.lpvData = lpSrcI->position.lpvData;
lpDevI->SOA.dwStride = lpSrcI->dwNumVertices;
lpDevI->dwSOAStartVertex = dwSrcIndex;
lpDevI->dwOutputSize = position.dwStride;
}
else
{ // AOS FVF
lpDevI->dwOutputSize = position.dwStride;
lpDevI->position.lpvData = LPVOID(LPBYTE(lpSrcI->position.lpvData) + dwSrcIndex * lpSrcI->position.dwStride);
lpDevI->position.dwStride = lpSrcI->position.dwStride;
}
}
lpDevI->dwFlags |= D3DPV_VBCALL;
D3DFE_ProcessVertices(lpDevI);
if (!(lpDevI->dwFlags & D3DDP_DONOTCLIP))
D3DFE_UpdateClipStatus(lpDevI);
// Unlock the VBs
Unlock();
lpSrcI->Unlock();
return D3D_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "IDirect3DDevice3::DrawIndexedPrimitiveVB"
HRESULT D3DAPI DIRECT3DDEVICEI::DrawIndexedPrimitiveVB(D3DPRIMITIVETYPE dptPrimitiveType,
LPDIRECT3DVERTEXBUFFER lpVBuf,
LPWORD lpwIndices, DWORD dwIndexCount,
DWORD dwFlags)
{
LPDIRECT3DVERTEXBUFFERI lpVBufI;
CLockD3DMT lockObject(this, DPF_MODNAME, REMIND("")); // Takes D3D lock.
HRESULT ret;
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(lpVBuf)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
lpVBufI = static_cast<LPDIRECT3DVERTEXBUFFERI>(lpVBuf);
if (!VALID_DIRECT3DDEVICE3_PTR(this)) {
D3D_ERR( "Invalid Direct3DDevice pointer" );
return DDERR_INVALIDOBJECT;
}
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
if (!IsDPFlagsValid(dwFlags))
{
D3D_ERR("Invalid Flags in dwFlags field");
return DDERR_INVALIDPARAMS;
}
if (!(IS_HW_DEVICE(this) || (lpVBufI->dwCaps & D3DVBCAPS_SYSTEMMEMORY)))
{
D3D_ERR("Cannot use vid mem vertex buffers with SW devices");
return DDERR_INVALIDPARAMS;
}
if (lpVBufI->dwLockCnt)
{
D3D_ERR("Cannot render using a locked vertex buffer");
return D3DERR_VERTEXBUFFERLOCKED;
}
if (this->dwNumPrimitives > MAX_DX6_PRIMCOUNT)
{
D3D_ERR("D3D for DX6 cannot handle greater that 64K sized primitives");
return D3DERR_TOOMANYPRIMITIVES;
}
Profile(PROF_DRAWINDEXEDPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf);
#else
lpVBufI = static_cast<LPDIRECT3DVERTEXBUFFERI>(lpVBuf);
#endif
this->dwFlags = dwFlags | lpVBufI->dwPVFlags;
this->primType = dptPrimitiveType;
this->dwNumVertices = lpVBufI->dwNumVertices;
this->dwNumIndices = dwIndexCount;
this->lpwIndices = lpwIndices;
GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives and update stats
this->dwFEFlags |= D3DFE_NEED_TEXTURE_UPDATE; // Need to call UpdateTextures()
if (lpVBufI->srcVOP & D3DVOP_RENDER)
{ // TLVERTEX
#if DBG
if (lpVBufI->fvf & D3DFVF_NORMAL)
{
D3D_ERR("The vertex buffer cannot be processed");
D3D_ERR("It has XYZRHW position type and normals");
return DDERR_INVALIDPARAMS;
}
#endif
this->dwOutputSize = lpVBufI->position.dwStride;
this->dwVIDOut = lpVBufI->fvf;
this->dwVIDIn = lpVBufI->fvf;
// needed for legacy drivers' DrawIndexPrim code
this->lpvOut = lpVBufI->position.lpvData;
if (IS_DP2HAL_DEVICE(this))
{
this->nTexCoord = lpVBufI->nTexCoord;
CDirect3DDeviceIDP2 *dev = static_cast<CDirect3DDeviceIDP2*>(this);
ret = dev->StartPrimVB(lpVBufI, 0);
lpVBufI->lpDevIBatched = this;
if (ret != D3D_OK)
return ret;
}
else
{
ComputeTCI2CopyLegacy(this, lpVBufI->nTexCoord, TRUE);
}
if (dwFlags & D3DDP_DONOTCLIP)
{
return DrawIndexPrim();
}
else
{
this->lpClipFlags = lpVBufI->clipCodes;
this->dwClipUnion = ~0; // Force clipping
// If lpvData is NULL, it is a driver allocated buffer which
// means IS_DPHAL_DEVICE() is true.
// We need to lock such a buffer only if we need to clip
if (!lpVBufI->position.lpvData)
{
// Lock VB
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ret = lpVBufI->lpDDSVB->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_READONLY | DDLOCK_NOSYSLOCK, NULL);
if (ret != DD_OK)
{
D3D_ERR("Could not lock vertex buffer.");
return ret;
}
this->lpvOut = ddsd.lpSurface;
// Draw with clipping
#if DBG
// To Do: Check vertices & clip flags against current viewport
this->position.lpvData = this->lpvOut; // Otherwise the check will fail
ret = CheckDrawIndexedPrimitive(this);
if (ret == D3D_OK)
ret = DoDrawIndexedPrimitive(this);
#else
ret = DoDrawIndexedPrimitive(this);
#endif
// Unlock VB
if (ret == D3D_OK)
return lpVBufI->lpDDSVB->Unlock(NULL);
else
lpVBufI->lpDDSVB->Unlock(NULL);
return ret;
}
else
{
// Draw with clipping
#if DBG
// To Do: Check vertices & clip flags against current viewport
this->position.lpvData = this->lpvOut; // Otherwise the check will fail
ret = CheckDrawIndexedPrimitive(this);
if (ret != D3D_OK)
return ret;
#endif
return DoDrawIndexedPrimitive(this);
}
}
}
else
{
this->dwVIDIn = lpVBufI->fvf;
if (lpVBufI->bReallyOptimized)
{
// Assume that SOA.lpvData is the same as position.lpvData
this->SOA.lpvData = lpVBufI->position.lpvData;
this->SOA.dwStride = lpVBufI->dwNumVertices;
this->dwSOAStartVertex = 0;
}
else
{
this->position = lpVBufI->position;
}
ComputeOutputFVF(this);
#if DBG
ret = CheckDrawIndexedPrimitive(this);
if (ret != D3D_OK)
return ret;
#endif
return this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM);
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "Direct3DDevice3::DrawPrimitiveVB"
HRESULT D3DAPI DIRECT3DDEVICEI::DrawPrimitiveVB(D3DPRIMITIVETYPE dptPrimitiveType,
LPDIRECT3DVERTEXBUFFER lpVBuf,
DWORD dwStartVertex, DWORD dwNumVertices,
DWORD dwFlags)
{
LPDIRECT3DVERTEXBUFFERI lpVBufI;
CLockD3DMT lockObject(this, DPF_MODNAME, REMIND("")); // Takes D3D lock
HRESULT ret;
#if DBG
/*
* validate parms
*/
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(lpVBuf)) {
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
lpVBufI = static_cast<LPDIRECT3DVERTEXBUFFERI>(lpVBuf);
if (!VALID_DIRECT3DDEVICE3_PTR(this)) {
D3D_ERR( "Invalid Direct3DDevice pointer" );
return DDERR_INVALIDOBJECT;
}
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
if (!IsDPFlagsValid(dwFlags) ||
(dwStartVertex + dwNumVertices) > lpVBufI->dwNumVertices)
return DDERR_INVALIDPARAMS;
if (!(IS_HW_DEVICE(this) || (lpVBufI->dwCaps & D3DVBCAPS_SYSTEMMEMORY)))
{
D3D_ERR("Cannot use vid mem vertex buffers with SW devices");
return DDERR_INVALIDPARAMS;
}
if (lpVBufI->dwLockCnt)
{
D3D_ERR("Cannot render using a locked vertex buffer");
return D3DERR_VERTEXBUFFERLOCKED;
}
if (dwNumVertices > MAX_DX6_VERTICES)
{
D3D_ERR("D3D for DX6 cannot handle greater than 64K vertices");
return D3DERR_TOOMANYVERTICES;
}
Profile(PROF_DRAWPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf);
#else
lpVBufI = static_cast<LPDIRECT3DVERTEXBUFFERI>(lpVBuf);
#endif
this->dwFlags = dwFlags | lpVBufI->dwPVFlags;
this->primType = dptPrimitiveType;
this->dwNumVertices = dwNumVertices;
this->dwNumIndices = 0;
this->lpwIndices = NULL;
GetNumPrim(this, dwNumVertices); // Calculate dwNumPrimitives and update stats
this->dwFEFlags |= D3DFE_NEED_TEXTURE_UPDATE; // Need to call UpdateTextures()
if (lpVBufI->srcVOP & D3DVOP_RENDER)
{ // TLVERTEX
#if DBG
if (lpVBufI->fvf & D3DFVF_NORMAL)
{
D3D_ERR("The vertex buffer cannot be processed");
D3D_ERR("It has XYZRHW position type and normals");
return DDERR_INVALIDPARAMS;
}
#endif
this->dwOutputSize = lpVBufI->position.dwStride;
this->dwVIDOut = lpVBufI->fvf;
// needed for legacy drivers' DrawPrim code
this->lpvOut = (BYTE*)(lpVBufI->position.lpvData) +
dwStartVertex * this->dwOutputSize;
if (IS_DP2HAL_DEVICE(this))
{
this->nTexCoord = lpVBufI->nTexCoord;
CDirect3DDeviceIDP2 *dev = static_cast<CDirect3DDeviceIDP2*>(this);
ret = dev->StartPrimVB(lpVBufI, dwStartVertex);
lpVBufI->lpDevIBatched = this;
if (ret != D3D_OK)
return ret;
}
else
{
ComputeTCI2CopyLegacy(this, lpVBufI->nTexCoord, TRUE);
}
if (dwFlags & D3DDP_DONOTCLIP)
{
return DrawPrim();
}
else
{
this->lpClipFlags = lpVBufI->clipCodes + dwStartVertex;
this->dwClipUnion = ~0; // Force clipping
// If lpvData is NULL, it is a driver allocated buffer which
// means IS_DPHAL_DEVICE() is true.
// We need to lock such a buffer only if we need to clip
if (!lpVBufI->position.lpvData)
{
// Lock VB
DDSURFACEDESC2 ddsd;
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
ddsd.dwSize = sizeof(DDSURFACEDESC2);
ret = lpVBufI->lpDDSVB->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_READONLY | DDLOCK_NOSYSLOCK, NULL);
if (ret != DD_OK)
{
D3D_ERR("Could not lock vertex buffer.");
return ret;
}
this->lpvOut = (BYTE*)(ddsd.lpSurface) +
dwStartVertex * this->dwOutputSize;
// Draw with clipping
#if DBG
// To Do: Check vertices & clip flags against current viewport
this->position.lpvData = this->lpvOut; // Otherwise the check will fail
ret=CheckDrawPrimitive(this);
if (ret == D3D_OK)
ret = DoDrawPrimitive(this);
#else
ret = DoDrawPrimitive(this);
#endif
// Unlock VB
if (ret == D3D_OK)
return lpVBufI->lpDDSVB->Unlock(NULL);
else
lpVBufI->lpDDSVB->Unlock(NULL);
return ret;
}
else
{
// Draw with clipping
#if DBG
// To Do: Check vertices & clip flags against current viewport
this->position.lpvData = this->lpvOut; // Otherwise the check will fail
ret=CheckDrawPrimitive(this);
if (ret != D3D_OK)
return ret;
#endif
return DoDrawPrimitive(this);
}
}
}
else
{
this->dwVIDIn = lpVBufI->fvf;
if (lpVBufI->bReallyOptimized)
{
// Assume that SOA.lpvData is the same as position.lpvData
this->SOA.lpvData = lpVBufI->position.lpvData;
this->SOA.dwStride = lpVBufI->dwNumVertices;
this->dwSOAStartVertex = dwStartVertex;
}
else
{
this->position.lpvData = (BYTE*)(lpVBufI->position.lpvData) +
dwStartVertex * lpVBufI->position.dwStride;
this->position.dwStride = lpVBufI->position.dwStride;
}
ComputeOutputFVF(this);
#if DBG
ret=CheckDrawPrimitive(this);
if (ret != D3D_OK)
return ret;
#endif
return this->ProcessPrimitive();
}
}
//---------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CDirect3DVertexBuffer::Optimize"
HRESULT D3DAPI CDirect3DVertexBuffer::Optimize(LPDIRECT3DDEVICE3 lpDevice, DWORD dwFlags)
{
HRESULT ret;
LPDIRECT3DDEVICEI lpDevI;
// Validate parms
//
TRY
{
if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this))
{
D3D_ERR( "Invalid Direct3DVertexBuffer pointer" );
return DDERR_INVALIDPARAMS;
}
if (!VALID_DIRECT3DDEVICE3_PTR(lpDevice))
{
D3D_ERR( "Invalid Direct3DDevice pointer" );
return DDERR_INVALIDOBJECT;
}
lpDevI = static_cast<LPDIRECT3DDEVICEI>(lpDevice);
}
EXCEPT( EXCEPTION_EXECUTE_HANDLER )
{
D3D_ERR( "Exception encountered validating parameters" );
return 0;
}
if (dwFlags != 0)
{
D3D_ERR("dwFlags should be zero");
return DDERR_INVALIDPARAMS;
}
CLockD3DMT lockObject(lpDevI, DPF_MODNAME, REMIND(""));
if (this->dwCaps & D3DVBCAPS_OPTIMIZED)
{
D3D_ERR("The vertex buffer already optimized");
return D3DERR_VERTEXBUFFEROPTIMIZED;
}
if (this->dwLockCnt != 0)
{
D3D_ERR("Could not optimize locked vertex buffer");
return D3DERR_VERTEXBUFFERLOCKED;
}
// Do nothing for transformed vertices
if ((this->fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
{
this->dwCaps |= D3DVBCAPS_OPTIMIZED;
return D3D_OK;
}
// Get the buffer size to allocate
DWORD bufferSize = lpDevI->pGeometryFuncs->ComputeOptimizedVertexBufferSize
(this->fvf, this->position.dwStride,
dwNumVertices);
// Create new surfaces for optimized vertex buffer
if (bufferSize == 0)
{
this->dwCaps |= D3DVBCAPS_OPTIMIZED;
return D3D_OK;
}
LPDIRECTDRAWSURFACE4 lpSurface4;
LPDIRECTDRAWSURFACE lpSurface;
LPVOID lpMemory;
ret = CreateMemoryBuffer(lpDevI->lpDirect3DI, &lpSurface4, &lpSurface,
&lpMemory, bufferSize,
clipCodes ? 0 : D3DDP_DONOTCLIP);
if (ret != D3D_OK)
return ret;
// Try to optimize
// If optimized vertex buffer are not supported by the implementation
// it returns E_NOTIMPL. In this case we still set D3DVBCAPS_OPTIMIZED to prevent
// locking of the vertex buffer. But bReallyOptimized is set to FALSE, to use
// the original buffer.
ret = lpDevI->pGeometryFuncs->OptimizeVertexBuffer
(fvf, dwNumVertices, position.dwStride, position.lpvData,
lpMemory, dwFlags);
if (ret)
{
lpSurface4->Release();
lpSurface->Release();
if (ret == E_NOTIMPL)
{
this->dwCaps |= D3DVBCAPS_OPTIMIZED;
return D3D_OK;
}
else
{
D3D_ERR("Failed to optimize vertex buffer");
return ret;
}
}
bReallyOptimized = TRUE;
legacyVertexType = (D3DVERTEXTYPE)0;
this->dwPVFlags |= D3DPV_SOA;
this->dwCaps |= D3DVBCAPS_OPTIMIZED;
// Destroy old surfaces
lpDDSVB->Release();
lpDDS1VB->Release();
// And use new ones
lpDDSVB = lpSurface4;
lpDDS1VB = lpSurface;
position.lpvData = lpMemory;
return D3D_OK;
}