/*==========================================================================; * * 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" #include "clipfunc.h" #include "pvvid.h" // The bit is set when a vertex buffer was the destination in a ProcessVerticesCall // with clipping enabled. We cannot pass such a buffer to TL HAL, because some vertices // could be in the screen space and some in the clipping space. There is no DDI to pass // clip codes together with a vertex buffer const DWORD D3DPV_CLIPCODESGENERATED = D3DPV_RESERVED2; const DWORD D3DVOP_RENDER = 1 << 31; const DWORD D3DVBCAPS_VALID = D3DVBCAPS_SYSTEMMEMORY | D3DVBCAPS_WRITEONLY | D3DVBCAPS_OPTIMIZED | D3DVBCAPS_DONOTCLIP; 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 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; } #endif *ppvObj = NULL; if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDirect3DVertexBuffer7)) { AddRef(); *ppvObj = static_cast(static_cast(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 if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) { D3D_ERR( "Invalid Direct3DVertexBuffer pointer" ); 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 if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) { D3D_ERR( "Invalid Direct3DVertexBuffer pointer" ); 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, LPDIRECT3DVERTEXBUFFER7 *lplpVBuf, DWORD dwFlags) { CDirect3DVertexBuffer* lpVBufI; HRESULT ret = D3D_OK; *lplpVBuf = NULL; lpVBufI = static_cast(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 = (LPDIRECT3DVERTEXBUFFER7)lpVBufI; return(D3D_OK); } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DI::CreateVertexBuffer" HRESULT D3DAPI DIRECT3DI::CreateVertexBuffer( LPD3DVERTEXBUFFERDESC lpDesc, LPDIRECT3DVERTEXBUFFER7* lplpVBuf, DWORD dwFlags) { CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock. #if DBG /* * validate parms */ if (!VALID_DIRECT3D_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 != 0) { 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; 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(); } } //--------------------------------------------------------------------- // // 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, LPDIRECTDRAWSURFACE7 *lplpSurface7, LPDIRECTDRAWSURFACE *lplpSurface, LPVOID *lplpMemory, DWORD dwBufferSize) { HRESULT ret; DDSURFACEDESC2 ddsd; memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); ddsd.dwFlags = DDSD_WIDTH | DDSD_CAPS | DDSD_FVF; ddsd.dwWidth = dwBufferSize; ddsd.ddsCaps.dwCaps = DDSCAPS_EXECUTEBUFFER; ddsd.ddsCaps.dwCaps2 = this->dwMemType; ddsd.dwFVF = this->fvf; // Let driver know about the FVF // The meaning of DDSCAPS_VIDEOMEMORY and DDSCAPS_SYSTEMEMORY are // slightly different in case of VBs. the former only means that // the buffer is driver 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 want DDraw to take the Win16 lock for locking system memory // surfaces. bool bTLHAL = DDGBL(lpD3DI)->lpD3DGlobalDriverData && (DDGBL(lpD3DI)->lpD3DGlobalDriverData->hwCaps.dwDevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT); if ((this->dwCaps & D3DVBCAPS_SYSTEMMEMORY) || !(bTLHAL || FVF_TRANSFORMED(fvf))) { // This VB cannot reside in driver friendly memory since either: // 1. The app explicitly specified system memory // 2. The vertex buffer is untransformed and it is not a T&L hal // thus 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->lpDD7->CreateSurface(&ddsd, lplpSurface7, 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 ((this->dwCaps & D3DVBCAPS_DONOTCLIP) || bTLHAL) ddsd.ddsCaps.dwCaps |= this->dwCaps & DDSCAPS_WRITEONLY; D3D_INFO(8, "Trying to create a vid mem vertex buffer"); #ifdef __DISABLE_VIDMEM_VBS__ if ((lpD3DI->bDisableVidMemVBs == TRUE) || (lpD3DI->lpDD7->CreateSurface(&ddsd, lplpSurface7, NULL) != DD_OK)) #else //__DISABLE_VIDMEM_VBS__ if (lpD3DI->lpDD7->CreateSurface(&ddsd, lplpSurface7, NULL) != DD_OK) #endif //__DISABLE_VIDMEM_VBS__ { // 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->lpDD7->CreateSurface(&ddsd, lplpSurface7, NULL); if (ret != DD_OK) { D3D_ERR("Could not allocate the Vertex buffer."); return ret; } } else { this->dwMemType = DDSCAPS_VIDEOMEMORY; // Stick in our pointer so that we can be notified about mode changes DDSLCL(*lplpSurface7)->lpSurfMore->lpVB = static_cast(this); } } ret = (*lplpSurface7)->QueryInterface(IID_IDirectDrawSurfaceNew, (LPVOID*)lplpSurface); if (ret != DD_OK) { D3D_ERR("failed to QI for DDS1"); return ret; } ret = (*lplpSurface7)->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL); if (ret != DD_OK) { D3D_ERR("Could not lock vertex buffer."); return ret; } *lplpMemory = ddsd.lpSurface; 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; #ifdef VTABLE_HACK // Copy with vtable lpVtbl = *((LPVOID**)this); memcpy(newVtbl, lpVtbl, sizeof(PVOID)*D3DVB_NUM_VIRTUAL_FUNCTIONS); // Point to the new one *((LPVOID*)this) = (LPVOID)newVtbl; #endif // VTABLE_HACK 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; } this->nTexCoord = FVF_TEXCOORD_NUMBER(fvf); this->dwTexCoordSizeTotal = ComputeTextureCoordSize(this->fvf, this->dwTexCoordSize); position.dwStride = GetVertexSizeFVF(this->fvf) + this->dwTexCoordSizeTotal; if (position.dwStride == 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; #ifdef DBG // Allocate space for one more vertex and fill with deadbeef. Used to check for // overwrites during unlock ret = CreateMemoryBuffer(lpD3DI, &lpDDSVB, &lpDDS1VB, &position.lpvData, position.dwStride * (dwNumVertices + 1)); if (ret != D3D_OK) return ret; LPDWORD pPad = (LPDWORD)((LPBYTE)(position.lpvData) + position.dwStride * dwNumVertices); for (unsigned i = 0; i < position.dwStride / sizeof(DWORD); ++i) *pPad++ = 0xdeadbeef; #else ret = CreateMemoryBuffer(lpD3DI, &lpDDSVB, &lpDDS1VB, &position.lpvData, position.dwStride * dwNumVertices); if (ret != D3D_OK) return ret; #endif /* Classify the operations that can be done using this VB */ if ((fvf & D3DFVF_POSITION_MASK)) { if ((fvf & D3DFVF_POSITION_MASK) != D3DFVF_XYZRHW) { D3D_INFO(4, "D3DFVF_XYZ set. Can be source VB for Transform"); srcVOP = D3DVOP_TRANSFORM | D3DVOP_EXTENTS | D3DVOP_CLIP; } else { D3D_INFO(4, "D3DFVF_XYZRHW set. Can be dest VB for Transform"); dstVOP = D3DVOP_TRANSFORM | D3DVOP_EXTENTS; srcVOP = D3DVOP_EXTENTS; if ((dwCaps & D3DVBCAPS_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 (srcVOP & D3DVOP_TRANSFORM) { D3D_INFO(4, "Can be src VB for lighting."); srcVOP |= D3DVOP_LIGHT; } 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; } 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 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; } } #endif if (this->dwCaps & D3DVBCAPS_OPTIMIZED) { D3D_ERR("Cannot lock optimized vertex buffer"); return(D3DERR_VERTEXBUFFEROPTIMIZED); } if (!this->position.lpvData) { // Unlock if previous lock was broken due to mode switch if (DDSGBL(lpDDSVB)->dwUsageCount > 0) { DDASSERT(DDSGBL(lpDDSVB)->dwUsageCount == 1); D3D_INFO(2, "Lock: Unlocking broken VB lock"); lpDDSVB->Unlock(NULL); } if (lpDevIBatched) { ret = lpDevIBatched->FlushStates(); if (ret != D3D_OK) { D3D_ERR("Could not flush batch referring to VB during Lock"); return ret; } } #ifdef DBG LPVOID pOldBuf = (LPVOID)((LPDDRAWI_DDRAWSURFACE_INT)lpDDSVB)->lpLcl->lpGbl->fpVidMem; #endif // DBG // Do a real Lock 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("Lock: Could not lock Vertex Buffer: %08x", ret); return ret; } position.lpvData = ddsd.lpSurface; #if DBG if(ddsd.lpSurface != pOldBuf) { D3D_INFO(2, "Driver swapped VB pointer in Lock"); } LPDWORD pPad = (LPDWORD)((LPBYTE)(position.lpvData) + position.dwStride * dwNumVertices); for (unsigned i = 0; i < position.dwStride / sizeof(DWORD); ++i) *pPad++ = 0xdeadbeef; #endif } #ifdef VTABLE_HACK /* Single threaded or Multi threaded app ? */ if (!(((LPDDRAWI_DIRECTDRAW_INT)lpDirect3DI->lpDD)->lpLcl->dwLocalFlags & DDRAWILCL_MULTITHREADED)) VtblLockFast(); #endif // VTABLE_HACK 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) { dwLockCnt++; D3D_INFO(6, "VB Lock: %lx Lock Cnt =%d", this, dwLockCnt); if (!(dwFlags & (DDLOCK_READONLY | DDLOCK_NOOVERWRITE)) && lpDevIBatched) { HRESULT ret; if (dwFlags & DDLOCK_OKTOSWAP) { ret = lpDevIBatched->FlushStatesReq(position.dwStride * dwNumVertices); #if DBG if (!(this->dwCaps & D3DVBCAPS_OPTIMIZED)) { // Make sure the size of the new buffer is the same DDASSERT(position.dwStride * (dwNumVertices + 1) <= DDSGBL(lpDDSVB)->dwLinearSize); // Write deadbeaf in the pad area LPDWORD pPad = (LPDWORD)((LPBYTE)(position.lpvData) + position.dwStride * dwNumVertices); for (unsigned i = 0; i < position.dwStride / sizeof(DWORD); ++i) *pPad++ = 0xdeadbeef; } #endif } else ret = lpDevIBatched->FlushStates(); if (ret != D3D_OK) { D3D_ERR("Could not flush batch referring to VB during Lock"); return ret; } } *lplpData = position.lpvData; if (lpdwSize) *lpdwSize = position.dwStride * dwNumVertices; return D3D_OK; } #undef DPF_MODNAME #define DPF_MODNAME "Direct3DVertexBuffer::Unlock" HRESULT D3DAPI CDirect3DVertexBuffer::Unlock() { if (dwLockCnt) { dwLockCnt--; } #ifdef DBG if (!(this->dwCaps & D3DVBCAPS_OPTIMIZED)) { // Check for VB overruns LPDWORD pPad = (LPDWORD)((LPBYTE)(position.lpvData) + position.dwStride * dwNumVertices); for (unsigned i = 0; i < position.dwStride / sizeof(DWORD); ++i) if (*pPad++ != 0xdeadbeef) { D3D_ERR("Vertex buffer was overrun. Make sure that you do not write past the VB size!"); return D3DERR_VERTEXBUFFERUNLOCKFAILED; } D3D_INFO(6, "VB Unlock: %lx Lock Cnt =%d", this, dwLockCnt); } #endif return D3D_OK; } // Called from FlushStates to undo cached VB pointer so that the next lock causes a driver lock // This is necessary if the we did not flush with SWAPVERTEXBUFFER. void CDirect3DVertexBuffer::UnlockI() { if ((this->dwMemType == DDSCAPS_VIDEOMEMORY) && (dwLockCnt == 0)) { #ifdef VTABLE_HACK VtblLockDefault(); #endif lpDDSVB->Unlock(NULL); position.lpvData = 0; } else if (dwLockCnt !=0 ) { D3D_WARN(4, "App has a lock on VB %08x so driver call may be slow", this); } } #ifndef WIN95 #undef DPF_MODNAME #define DPF_MODNAME "Direct3DVertexBuffer::LockWorkAround" HRESULT CDirect3DVertexBuffer::LockWorkAround(CDirect3DDeviceIDP2 *pDev) { if (this->dwMemType == DDSCAPS_VIDEOMEMORY) { #ifdef DBG LPVOID pOldBuf = (LPVOID)((LPDDRAWI_DDRAWSURFACE_INT)lpDDSVB)->lpLcl->lpGbl->fpVidMem; #endif // DBG // Do a real Lock DDSURFACEDESC2 ddsd; memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); HRESULT ret = lpDDSVB->Lock(NULL, &ddsd, DDLOCK_OKTOSWAP | DDLOCK_NOSYSLOCK, NULL); if (ret != DD_OK) { D3D_ERR("Lock: Could not lock Vertex Buffer: %08x", ret); return ret; } position.lpvData = ddsd.lpSurface; pDev->alignedBuf = ddsd.lpSurface; #ifdef DBG if(ddsd.lpSurface != pOldBuf) { D3D_INFO(2, "Driver swapped TLVBuf pointer in Lock"); } #endif // Make sure the size of the new buffer is the same DDASSERT(position.dwStride * (dwNumVertices + 1) <= DDSGBL(lpDDSVB)->dwLinearSize); } return D3D_OK; } #undef DPF_MODNAME #define DPF_MODNAME "Direct3DVertexBuffer::UnlockWorkAround" void CDirect3DVertexBuffer::UnlockWorkAround() { if ((this->dwMemType == DDSCAPS_VIDEOMEMORY) && (position.lpvData != 0)) { lpDDSVB->Unlock(NULL); position.lpvData = 0; } } #endif // WIN95 // Cause us to go thru the slow path and force a lock // The slow path will do the unlock if necessary. This // is because we are called from DDraw's invalidate // surface code and it might not be the best time to // call back ddraw to unlock the surface. void CDirect3DVertexBuffer::BreakLock() { D3D_INFO(6, "Notified of restore on VB %08x", this); #ifdef VTABLE_HACK VtblLockDefault(); #endif position.lpvData = 0; } #undef DPF_MODNAME #define DPF_MODNAME "Direct3DVertexBuffer::GetVertexBufferDesc" HRESULT D3DAPI CDirect3DVertexBuffer::GetVertexBufferDesc(LPD3DVERTEXBUFFERDESC lpDesc) { #if DBG 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; } #endif lpDesc->dwCaps = dwCaps; lpDesc->dwFVF = fvf; lpDesc->dwNumVertices = this->dwNumVertices; return D3D_OK; } //--------------------------------------------------------------------- // Common validation for ProcessVertices and ProcessVerticesStrided // HRESULT CDirect3DVertexBuffer::ValidateProcessVertices( DWORD vertexOP, DWORD dwDstIndex, DWORD dwCount, LPVOID lpSrc, LPDIRECT3DDEVICE7 lpDevice, DWORD dwFlags) { if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) { D3D_ERR( "Invalid destination Direct3DVertexBuffer pointer" ); return DDERR_INVALIDPARAMS; } if (!VALID_DIRECT3DDEVICE_PTR(lpDevice)) { D3D_ERR( "Invalid Direct3DDevice pointer" ); return DDERR_INVALIDOBJECT; } LPDIRECT3DDEVICEI lpDevI = static_cast(lpDevice); if (lpDevI->ValidateFVF(this->fvf) != D3D_OK) { D3D_ERR("Invalid vertex buffer FVF for the device"); return DDERR_INVALIDPARAMS; } if (dwFlags & ~D3DPV_DONOTCOPYDATA) { D3D_ERR( "Invalid dwFlags set" ); return DDERR_INVALIDPARAMS; } if ((dwDstIndex + dwCount) > this->dwNumVertices) { D3D_ERR( "Vertex count plus destination index is greater than number of vertices" ); return DDERR_INVALIDPARAMS; } // Validate Dst Vertex Formats if (lpSrc) { if ((this->dstVOP & vertexOP) != vertexOP) goto error; } else { if ((this->fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW) { if (vertexOP & ~(D3DVOP_CLIP | D3DVOP_EXTENTS)) goto error; } else { if (vertexOP & ~(D3DVOP_CLIP)) goto error; } } return D3D_OK; error: D3D_ERR("Destination VB cannot support this operation"); return D3DERR_INVALIDVERTEXFORMAT; } //--------------------------------------------------------------------- // Common part for ProcessVertices and ProcessVerticesStrided // HRESULT CDirect3DVertexBuffer::DoProcessVertices( LPDIRECT3DVERTEXBUFFERI lpSrcI, LPDIRECT3DDEVICEI lpDevI, DWORD vertexOP, DWORD dwSrcIndex, DWORD dwDstIndex, DWORD dwFlags) { lpDevI->lpClipFlags = clipCodes + dwDstIndex; // Compute needed output FVF { DWORD dwInputVertexSize; HRESULT ret = lpDevI->SetupFVFDataCommon(&dwInputVertexSize); if (ret != D3D_OK) return ret; // Make sure we have specular in output VB if the current state settings // require us to write to specular if (vertexOP & D3DVOP_LIGHT) if (lpDevI->rstates[D3DRENDERSTATE_SPECULARENABLE] || lpDevI->rstates[D3DRENDERSTATE_FOGENABLE]) if (!(fvf & D3DFVF_SPECULAR)) { D3D_ERR("Destination VB FVF format cannot be used with the current D3D settings"); return D3DERR_INVALIDVERTEXFORMAT; } // Check number of texture coordinates and texture formats in the // destination VB are the same as in the computed FVF DWORD dwComputedOutFVF = lpDevI->dwVIDOut & 0xFFFF0000; if (lpDevI->nOutTexCoord > this->nTexCoord || ((fvf & dwComputedOutFVF) != dwComputedOutFVF)) { D3D_ERR("Destination VB FVF format cannot be used with the current D3D settings"); return D3DERR_INVALIDVERTEXFORMAT; } } // Output lpDevI->lpvOut = LPVOID(LPBYTE(position.lpvData) + dwDstIndex * position.dwStride); lpDevI->dwOutputSize = this->position.dwStride; lpDevI->dwVIDOut = fvf; // Set up vertex pointers, because SetupFVFData works with "computed" FVF UpdateGeometryLoopData(lpDevI); // Save current flags to restore later DWORD dwOrigDeviceFlags = lpDevI->dwDeviceFlags; if (vertexOP & D3DVOP_CLIP) { lpDevI->dwDeviceFlags &= ~D3DDEV_DONOTCLIP; this->dwPVFlags |= D3DPV_CLIPCODESGENERATED; } else { lpDevI->dwDeviceFlags |= D3DDEV_DONOTCLIP; } if (vertexOP & D3DVOP_LIGHT) lpDevI->dwDeviceFlags |= D3DDEV_LIGHTING; else lpDevI->dwDeviceFlags &= ~D3DDEV_LIGHTING; if (vertexOP & D3DVOP_EXTENTS) { lpDevI->dwDeviceFlags &= ~D3DDEV_DONOTUPDATEEXTENTS; } else { lpDevI->dwDeviceFlags |= D3DDEV_DONOTUPDATEEXTENTS; } DoUpdateState(lpDevI); if (lpSrcI) { 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; } } if (dwFlags & D3DPV_DONOTCOPYDATA) { lpDevI->dwFlags |= D3DPV_DONOTCOPYDIFFUSE | D3DPV_DONOTCOPYSPECULAR | D3DPV_DONOTCOPYTEXTURE; // If D3DIM generates colors or texture, we should clear DONOTCOPY bits if (lpDevI->dwFlags & D3DPV_LIGHTING) { lpDevI->dwFlags &= ~D3DPV_DONOTCOPYDIFFUSE; if (lpDevI->dwDeviceFlags & D3DDEV_SPECULARENABLE) lpDevI->dwFlags &= ~D3DPV_DONOTCOPYSPECULAR; } if (lpDevI->dwFlags & D3DPV_FOG) lpDevI->dwFlags &= ~D3DPV_DONOTCOPYSPECULAR; // If front-end is asked to do something with texture coordinates // we disable DONOTCOPYTEXTURE if (__TEXTURETRANSFORMENABLED(lpDevI) || lpDevI->dwFlags2 & __FLAGS2_TEXGEN) { lpDevI->dwFlags &= ~D3DPV_DONOTCOPYTEXTURE; } } lpDevI->pGeometryFuncs->ProcessVertices(lpDevI); // This bit should be cleared, because for ProcessVertices calls user should // set texture stage indices and wrap modes himself lpDevI->dwDeviceFlags &= ~D3DDEV_REMAPTEXTUREINDICES; if (!(lpDevI->dwDeviceFlags & D3DDEV_DONOTCLIP)) D3DFE_UpdateClipStatus(lpDevI); // Restore _DONOTCLIP & _DONOTUPDATEEXTENTS flags const DWORD PRESERVED_FLAGS = D3DDEV_DONOTCLIP | D3DDEV_DONOTUPDATEEXTENTS | D3DDEV_LIGHTING; lpDevI->dwDeviceFlags = (dwOrigDeviceFlags & PRESERVED_FLAGS) | (lpDevI->dwDeviceFlags & ~PRESERVED_FLAGS); // Force recompute fvf next time around lpDevI->ForceFVFRecompute(); // Unlock the VB Unlock(); // If we used SOA then the dwVIDIn <-> position.dwStride relationship // violated. This fixes that. This is required since in non VB code // we will not recompute position.dwStride if FVF matched dwVIDIn. if (lpSrcI) lpDevI->position.dwStride = lpSrcI->position.dwStride; return D3D_OK; } //--------------------------------------------------------------------- // lpSrc should be NULL for XYZRHW buffers // #undef DPF_MODNAME #define DPF_MODNAME "Direct3DVertexBuffer::ProcessVertices" HRESULT D3DAPI CDirect3DVertexBuffer::ProcessVertices(DWORD vertexOP, DWORD dwDstIndex, DWORD dwCount, LPDIRECT3DVERTEXBUFFER7 lpSrc, DWORD dwSrcIndex, LPDIRECT3DDEVICE7 lpDevice, DWORD dwFlags) { LPDIRECT3DVERTEXBUFFERI lpSrcI; LPDIRECT3DDEVICEI lpDevI; HRESULT ret = D3D_OK; #if DBG ret = this->ValidateProcessVertices(vertexOP, dwDstIndex, dwCount, lpSrc, lpDevice, dwFlags); if (ret != D3D_OK) return ret; lpDevI = static_cast(lpDevice); if (lpSrc != NULL) { if (!VALID_DIRECT3DVERTEXBUFFER_PTR(lpSrc)) { D3D_ERR( "Invalid Direct3DVertexBuffer pointer" ); return DDERR_INVALIDPARAMS; } lpSrcI = static_cast(lpSrc); if (lpDevI->ValidateFVF(lpSrcI->fvf) != D3D_OK) { D3D_ERR("Invalid source vertex buffer FVF for the device"); return DDERR_INVALIDPARAMS; } // Validate Src Vertex Formats if ((lpSrcI->srcVOP & vertexOP) != vertexOP) { D3D_ERR("Source VB cannot support this operation"); return D3DERR_INVALIDVERTEXFORMAT; } if ((dwSrcIndex + dwCount) > lpSrcI->dwNumVertices) { D3D_ERR( "Source index plus vertex count is greater than number of vertices" ); return DDERR_INVALIDPARAMS; } if (!(vertexOP & D3DVOP_TRANSFORM)) { D3D_ERR("D3DVOP_TRANSFORM flag should be set"); return DDERR_INVALIDPARAMS; } // Source to ProcessVertices must be in system memory. This is for reasons similar // to why we insist on sys mem VB for SW rast. For instance, a driver may have optimized // the VB into some cryptic format which D3D FE will have no clue to decipher. if (!(lpSrcI->dwCaps & D3DVBCAPS_SYSTEMMEMORY)) { D3D_ERR("Source VB must be created with D3DVBCAPS_SYSTEMMEMORY"); return DDERR_INVALIDPARAMS; } } #else lpSrcI = static_cast(lpSrc); lpDevI = static_cast(lpDevice); #endif CLockD3DMT lockObject(lpDevI, DPF_MODNAME, REMIND("")); // Takes D3D lock. lpDevI->dwNumVertices = dwCount; // Lock the VBs LPVOID lpVoid; // We call the API level lock since dest VB may be in vid mem. This function will fail for // optimized VBs and that is OK since we cannot write out optimized vertices anyway. ret = Lock(DDLOCK_WAIT, &lpVoid, NULL); if (ret != D3D_OK) { D3D_ERR("Could not lock the vertex buffer"); return ret; } if (lpSrc == NULL) { lpDevI->lpvOut = LPVOID(LPBYTE(position.lpvData) + dwDstIndex * position.dwStride); if ((fvf & D3DFVF_POSITION_MASK) != D3DFVF_XYZRHW) { if (vertexOP & D3DVOP_CLIP) { CD3DFPstate D3DFPstate; // Sets optimal FPU state for D3D. if (lpDevI->dwFEFlags & (D3DFE_TRANSFORM_DIRTY | D3DFE_CLIPPLANES_DIRTY)) { DoUpdateState(lpDevI); } lpDevI->CheckClipStatus((D3DVALUE*)lpDevI->lpvOut, position.dwStride, dwCount, &lpDevI->dwClipUnion, &lpDevI->dwClipIntersection); D3DFE_UpdateClipStatus(lpDevI); } } else { // For transformed vertices we support only clip code generation and extens lpDevI->lpClipFlags = clipCodes + dwDstIndex; lpDevI->position.lpvData = lpDevI->lpvOut; lpDevI->position.dwStride = position.dwStride; lpDevI->dwOutputSize = position.dwStride; if (vertexOP & D3DVOP_CLIP) { D3DFE_GenClipFlags(lpDevI); D3DFE_UpdateClipStatus(lpDevI); // Mark this buffer as "transformed" for clipping dwPVFlags |= D3DPV_TLVCLIP; } if (vertexOP & D3DVOP_EXTENTS) { D3DFE_updateExtents(lpDevI); } } Unlock(); return D3D_OK; } // Safe to LockI since source is guaranteed to be in system memory // Cannot call API Lock since we need to be able to lock optimized VBs ret = lpSrcI->LockI(DDLOCK_WAIT | DDLOCK_READONLY, &lpVoid, NULL); if (ret != D3D_OK) { D3D_ERR("Could not lock the vertex buffer"); return ret; } dwPVFlags &= ~D3DPV_TLVCLIP; // Mark the dest VB as "not transformed" for clipping lpDevI->dwFlags = (lpSrcI->dwPVFlags & D3DPV_SOA) | D3DPV_VBCALL; lpDevI->dwDeviceFlags &= ~D3DDEV_STRIDE; // Input lpDevI->dwVIDIn = lpSrcI->fvf; ret = this->DoProcessVertices(lpSrcI, lpDevI, vertexOP, dwSrcIndex, dwDstIndex, dwFlags); if (ret != D3D_OK) lpSrcI->Unlock(); else ret = lpSrc->Unlock(); return ret; } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "Direct3DVertexBuffer::ProcessVerticesStrided" HRESULT D3DAPI CDirect3DVertexBuffer::ProcessVerticesStrided(DWORD vertexOP, DWORD dwDstIndex, DWORD dwCount, LPD3DDRAWPRIMITIVESTRIDEDDATA lpDrawData, DWORD dwSrcFVF, LPDIRECT3DDEVICE7 lpDevice, DWORD dwFlags) { LPDIRECT3DDEVICEI lpDevI; HRESULT ret = D3D_OK; #if DBG ret = this->ValidateProcessVertices(vertexOP, dwDstIndex, dwCount, lpDrawData, lpDevice, dwFlags); if (ret != D3D_OK) return ret; lpDevI = static_cast(lpDevice); if (lpDevI->ValidateFVF(dwSrcFVF) != D3D_OK) { D3D_ERR("Invalid source FVF for the device"); return DDERR_INVALIDPARAMS; } if ((dwSrcFVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW) { D3D_ERR("ProcessVerticesStrided cannot handle transformed vertices"); return D3DERR_INVALIDVERTEXTYPE; } if (!(vertexOP & D3DVOP_TRANSFORM)) { D3D_ERR("D3DVOP_TRANSFORM flag should be set"); return DDERR_INVALIDPARAMS; } #else lpDevI = static_cast(lpDevice); #endif CLockD3DMT lockObject(lpDevI, DPF_MODNAME, REMIND("")); // Takes D3D lock. // Lock the VBs LPVOID lpVoid; // We call the API level lock since dest VB may be in vid mem. This function will fail for // optimized VBs and that is OK since we cannot write out optimized vertices anyway. ret = Lock(DDLOCK_WAIT, &lpVoid, NULL); if (ret != D3D_OK) { D3D_ERR("Could not lock the vertex buffer"); return ret; } dwPVFlags &= ~D3DPV_TLVCLIP; // Mark the dest VB as "not transformed" for clipping lpDevI->dwDeviceFlags |= D3DDEV_STRIDE; lpDevI->dwFlags = D3DPV_VBCALL; // Input lpDevI->dwNumVertices = dwCount; lpDevI->dwVIDIn = dwSrcFVF; lpDevI->position = lpDrawData->position; lpDevI->normal = lpDrawData->normal; lpDevI->diffuse = lpDrawData->diffuse; lpDevI->specular = lpDrawData->specular; for (DWORD i=0; i < this->nTexCoord; i++) lpDevI->textures[i] = lpDrawData->textureCoords[i]; return this->DoProcessVertices(NULL, lpDevI, vertexOP, 0, dwDstIndex, dwFlags); } //--------------------------------------------------------------------- #ifdef DBG HRESULT DIRECT3DDEVICEI::CheckDrawPrimitiveVB(LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, DWORD dwFlags) { LPDIRECT3DVERTEXBUFFERI lpVBufI; if (!VALID_DIRECT3DVERTEXBUFFER_PTR(lpVBuf)) { D3D_ERR( "Invalid Direct3DVertexBuffer pointer" ); return DDERR_INVALIDPARAMS; } lpVBufI = static_cast(lpVBuf); if (!VALID_DIRECT3DDEVICE_PTR(this)) { D3D_ERR( "Invalid Direct3DDevice pointer" ); return DDERR_INVALIDOBJECT; } if (this->ValidateFVF(lpVBufI->fvf) != D3D_OK) { D3D_ERR("Invalid vertex buffer FVF for the device"); return DDERR_INVALIDPARAMS; } if (!IsDPFlagsValid(dwFlags)) { D3D_ERR("Invalid Flags in dwFlags field"); return DDERR_INVALIDPARAMS; } if (!(dwDeviceFlags & D3DDEV_DONOTCLIP) && (lpVBufI->clipCodes == NULL) && (lpVBufI->srcVOP & D3DVOP_RENDER)) { D3D_ERR("Vertex buffer does not support clipping"); 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 we are on HAL with an untransformed vid mem VB then we disallow This will happen only on T&L HW. The reason we disallow this is that it'll be very slow so this is not an interesting thing to do anyway */ if ( !IS_TLHAL_DEVICE(this) && !(lpVBufI->dwCaps & D3DVBCAPS_SYSTEMMEMORY) && !FVF_TRANSFORMED(lpVBufI->fvf) ) { D3D_ERR("DrawPrimitiveVB: Untransformed VB for HAL device must be created with D3DVBCAPS_SYSTEMMEMORY"); return DDERR_INVALIDPARAMS; } if (lpVBufI->dwLockCnt) { D3D_ERR("Cannot render using a locked vertex buffer"); return D3DERR_VERTEXBUFFERLOCKED; } if (dwStartVertex + dwNumVertices > lpVBufI->dwNumVertices) { D3D_ERR("Vertex range is outside the vertex buffer"); return DDERR_INVALIDPARAMS; } return D3D_OK; } #endif //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::DrawIndexedPrimitiveVB" HRESULT D3DAPI DIRECT3DDEVICEI::DrawIndexedPrimitiveVB(D3DPRIMITIVETYPE dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, LPWORD lpwIndices, DWORD dwIndexCount, DWORD dwFlags) { CLockD3DMT lockObject(this, DPF_MODNAME, REMIND("")); // Takes D3D lock. HRESULT ret; LPDIRECT3DVERTEXBUFFERI lpVBufI = static_cast(lpVBuf); #if DBG ret = CheckDrawPrimitiveVB(lpVBuf, dwStartVertex, dwNumVertices, dwFlags); if (ret != D3D_OK) return ret; Profile(PROF_DRAWINDEXEDPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf); #endif this->dwFlags = dwFlags | lpVBufI->dwPVFlags; this->primType = dptPrimitiveType; this->dwNumVertices = dwNumVertices; this->dwNumIndices = dwIndexCount; this->lpwIndices = lpwIndices; GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives and update stats #if DBG if (dwNumPrimitives > MAX_DX6_PRIMCOUNT) { D3D_ERR("D3D for DX7 cannot handle greater that 64K sized primitives"); return D3DERR_TOOMANYPRIMITIVES; } #endif if (lpVBufI->srcVOP & D3DVOP_RENDER || IS_TLHAL_DEVICE(this)) { // TLVERTEX or TLHAL this->dwOutputSize = lpVBufI->position.dwStride; this->position.dwStride = lpVBufI->position.dwStride; this->dwVIDOut = lpVBufI->fvf; DWORD dwOldVidIn = this->dwVIDIn; this->dwVIDIn = lpVBufI->fvf; BOOL bNoClipping = this->dwDeviceFlags & D3DDEV_DONOTCLIP || (!(lpVBufI->dwPVFlags & D3DPV_CLIPCODESGENERATED) && IS_TLHAL_DEVICE(this)); if (IS_DP2HAL_DEVICE(this)) { this->nTexCoord = lpVBufI->nTexCoord; CDirect3DDeviceIDP2 *dev = static_cast(this); ret = dev->StartPrimVB(lpVBufI, dwStartVertex); if (ret != D3D_OK) return ret; lpVBufI->lpDevIBatched = this; #ifdef VTABLE_HACK if (bNoClipping && !IS_MT_DEVICE(this)) VtblDrawIndexedPrimitiveVBTL(); #endif this->nOutTexCoord = lpVBufI->nTexCoord; } else { // needed for legacy drivers' DrawIndexPrim code this->lpvOut = (BYTE*)(lpVBufI->position.lpvData) + dwStartVertex * this->dwOutputSize; ComputeTCI2CopyLegacy(this, lpVBufI->nTexCoord, lpVBufI->dwTexCoordSize, TRUE); } if (bNoClipping) { return DrawIndexPrim(); } else { this->dwTextureCoordSizeTotal = lpVBufI->dwTexCoordSizeTotal; for (DWORD i=0; i < this->nOutTexCoord; i++) { this->dwTextureCoordSize[i] = lpVBufI->dwTexCoordSize[i]; } this->lpClipFlags = lpVBufI->clipCodes + dwStartVertex; this->dwClipUnion = ~0; // Force clipping if (dwOldVidIn != lpVBufI->fvf) { ComputeOutputVertexOffsets(this); } // 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 this->position.lpvData = this->lpvOut; #if DBG ret = CheckDrawIndexedPrimitive(this, dwStartVertex); 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 this->lpvOut = (BYTE*)lpVBufI->position.lpvData + dwStartVertex * this->dwOutputSize; this->position.lpvData = this->lpvOut; #if DBG ret = CheckDrawIndexedPrimitive(this, dwStartVertex); if (ret != D3D_OK) return ret; #endif return DoDrawIndexedPrimitive(this); } } } else { 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; #ifdef VTABLE_HACK if (IS_DP2HAL_DEVICE(this) && !IS_MT_DEVICE(this)) { CDirect3DDeviceIDP2 *dev = static_cast(this); dev->lpDP2LastVBI = lpVBufI; dev->VtblDrawIndexedPrimitiveVBFE(); } #endif } if (this->dwVIDIn != lpVBufI->fvf || this->dwDeviceFlags & D3DDEV_STRIDE) { this->dwDeviceFlags &= ~D3DDEV_STRIDE; this->dwVIDIn = lpVBufI->fvf; ret = SetupFVFData(NULL); if (ret != D3D_OK) goto l_exit; } #if DBG ret = CheckDrawIndexedPrimitive(this, dwStartVertex); if (ret != D3D_OK) goto l_exit; #endif ret = this->ProcessPrimitive(__PROCPRIMOP_INDEXEDPRIM); l_exit: // If we used SOA then the dwVIDIn <-> position.dwStride relationship // violated. This fixes that. This is required since in non VB code // we will not recompute position.dwStride if FVF matched dwVIDIn. this->position.dwStride = lpVBufI->position.dwStride; return ret; } } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::DrawPrimitiveVB" HRESULT D3DAPI DIRECT3DDEVICEI::DrawPrimitiveVB(D3DPRIMITIVETYPE dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, DWORD dwFlags) { CLockD3DMT lockObject(this, DPF_MODNAME, REMIND("")); // Takes D3D lock HRESULT ret; LPDIRECT3DVERTEXBUFFERI lpVBufI = static_cast(lpVBuf); #if DBG ret = CheckDrawPrimitiveVB(lpVBuf, dwStartVertex, dwNumVertices, dwFlags); if (ret != D3D_OK) return ret; Profile(PROF_DRAWPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf); #endif this->dwFlags = dwFlags | lpVBufI->dwPVFlags; this->primType = dptPrimitiveType; this->dwNumVertices = dwNumVertices; GetNumPrim(this, dwNumVertices); // Calculate dwNumPrimitives and update stats #if DBG if (dwNumPrimitives > MAX_DX6_PRIMCOUNT) { D3D_ERR("D3D for DX7 cannot handle greater that 64K sized primitives"); return D3DERR_TOOMANYPRIMITIVES; } #endif if (lpVBufI->srcVOP & D3DVOP_RENDER || IS_TLHAL_DEVICE(this)) { // TLVERTEX or TLHAL this->position.dwStride = lpVBufI->position.dwStride; this->dwOutputSize = lpVBufI->position.dwStride; DWORD dwOldVidIn = this->dwVIDIn; this->dwVIDIn = lpVBufI->fvf; this->dwVIDOut = lpVBufI->fvf; BOOL bNoClipping = this->dwDeviceFlags & D3DDEV_DONOTCLIP || (!(lpVBufI->dwPVFlags & D3DPV_CLIPCODESGENERATED) && IS_TLHAL_DEVICE(this)); if (IS_DP2HAL_DEVICE(this)) { this->nTexCoord = lpVBufI->nTexCoord; CDirect3DDeviceIDP2 *dev = static_cast(this); ret = dev->StartPrimVB(lpVBufI, dwStartVertex); if (ret != D3D_OK) return ret; lpVBufI->lpDevIBatched = this; #ifdef VTABLE_HACK if (bNoClipping && !IS_MT_DEVICE(this)) VtblDrawPrimitiveVBTL(); #endif this->nOutTexCoord = lpVBufI->nTexCoord; } else { // needed for legacy drivers' DrawPrim code this->lpvOut = (BYTE*)(lpVBufI->position.lpvData) + dwStartVertex * this->dwOutputSize; ComputeTCI2CopyLegacy(this, lpVBufI->nTexCoord, lpVBufI->dwTexCoordSize, TRUE); } if (bNoClipping) { return DrawPrim(); } else { this->dwTextureCoordSizeTotal = lpVBufI->dwTexCoordSizeTotal; for (DWORD i=0; i < this->nOutTexCoord; i++) { this->dwTextureCoordSize[i] = lpVBufI->dwTexCoordSize[i]; } this->lpClipFlags = lpVBufI->clipCodes + dwStartVertex; this->dwClipUnion = ~0; // Force clipping if (dwOldVidIn != lpVBufI->fvf) { ComputeOutputVertexOffsets(this); } // 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 this->position.lpvData = this->lpvOut; #if DBG 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 this->lpvOut = (BYTE*)lpVBufI->position.lpvData + dwStartVertex * this->dwOutputSize; this->position.lpvData = this->lpvOut; #if DBG ret=CheckDrawPrimitive(this); if (ret != D3D_OK) return ret; #endif return DoDrawPrimitive(this); } } } else { 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; #ifdef VTABLE_HACK if (IS_DP2HAL_DEVICE(this) && !IS_MT_DEVICE(this) && IS_FPU_SETUP(this)) { CDirect3DDeviceIDP2 *dev = static_cast(this); dev->lpDP2LastVBI = lpVBufI; dev->VtblDrawPrimitiveVBFE(); } #endif } if (this->dwVIDIn != lpVBufI->fvf || this->dwDeviceFlags & D3DDEV_STRIDE) { this->dwDeviceFlags &= ~D3DDEV_STRIDE; this->dwVIDIn = lpVBufI->fvf; ret = SetupFVFData(NULL); if (ret != D3D_OK) goto l_exit; } #if DBG ret=CheckDrawPrimitive(this); if (ret != D3D_OK) goto l_exit; #endif ret = this->ProcessPrimitive(); l_exit: // If we used SOA then the dwVIDIn <-> position.dwStride relationship // violated. This fixes that. This is required since in non VB code // we will not recompute position.dwStride if FVF matched dwVIDIn. this->position.dwStride = lpVBufI->position.dwStride; return ret; } } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "CDirect3DVertexBuffer::Optimize" HRESULT D3DAPI CDirect3DVertexBuffer::Optimize(LPDIRECT3DDEVICE7 lpDevice, DWORD dwFlags) { HRESULT ret; LPDIRECT3DDEVICEI lpDevI; DWORD bufferSize; LPDIRECTDRAWSURFACE7 lpSurface7; LPDIRECTDRAWSURFACE lpSurface; LPVOID lpMemory; // Validate parms if (!VALID_DIRECT3DVERTEXBUFFER_PTR(this)) { D3D_ERR( "Invalid Direct3DVertexBuffer pointer" ); return DDERR_INVALIDPARAMS; } if (!VALID_DIRECT3DDEVICE_PTR(lpDevice)) { D3D_ERR( "Invalid Direct3DDevice pointer" ); return DDERR_INVALIDOBJECT; } lpDevI = static_cast(lpDevice); if (dwFlags != 0) { D3D_ERR("dwFlags should be zero"); return DDERR_INVALIDPARAMS; } CLockD3DMT lockObject(lpDevI, DPF_MODNAME, REMIND("")); if (lpDevI->ValidateFVF(this->fvf) != D3D_OK) { D3D_ERR("Invalid vertex buffer FVF for the device"); return DDERR_INVALIDPARAMS; } 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; } if (IS_TLHAL_DEVICE(lpDevI) && (this->dwCaps & D3DVBCAPS_SYSTEMMEMORY)==0) { if (this->dwPVFlags & D3DPV_CLIPCODESGENERATED || (!IS_HW_DEVICE(lpDevI))) { // silently ignore since we'll be either // using our front end or this is ref rast // Either way we need no special optimization goto success; } DDSURFACEDESC2 ddsd; memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); ddsd.dwFlags = DDSD_CAPS | DDSD_FVF | DDSD_SRCVBHANDLE; ddsd.ddsCaps.dwCaps = DDSCAPS_EXECUTEBUFFER; ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; ddsd.ddsCaps.dwCaps2 = DDSCAPS2_VERTEXBUFFER; ddsd.dwFVF = this->fvf; // Let driver know about the FVF ddsd.dwSrcVBHandle = DDSLCL(this->lpDDSVB)->lpSurfMore->dwSurfaceHandle; if (lpDevI->lpDirect3DI->lpDD7->CreateSurface(&ddsd, &lpSurface7, NULL) != DD_OK) { // Driver could not or did not want to optimize the VB goto success; } ret = lpSurface7->QueryInterface(IID_IDirectDrawSurfaceNew, (LPVOID*)&lpSurface); if (ret != DD_OK) { D3D_ERR("failed to QI for DDS1"); lpSurface7->Release(); return ret; } // Destroy old surfaces lpDDSVB->Release(); lpDDS1VB->Release(); // And use new ones lpDDSVB = lpSurface7; lpDDS1VB = lpSurface; this->dwCaps |= D3DVBCAPS_OPTIMIZED; #ifdef VTABLE_HACK VtblLockDefault(); #endif // VTABLE_HACK return D3D_OK; } else { // Do nothing for transformed vertices if ((this->fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW) { goto success; } // Get the buffer size to allocate bufferSize = lpDevI->pGeometryFuncs->ComputeOptimizedVertexBufferSize (this->fvf, this->position.dwStride, dwNumVertices); // Create new surfaces for optimized vertex buffer if (bufferSize == 0) { goto success; } ret = CreateMemoryBuffer(lpDevI->lpDirect3DI, &lpSurface7, &lpSurface, &lpMemory, bufferSize); 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) { lpSurface7->Release(); lpSurface->Release(); if (ret == E_NOTIMPL) { goto success; } else { D3D_ERR("Failed to optimize vertex buffer"); return ret; } } bReallyOptimized = TRUE; this->dwPVFlags |= D3DPV_SOA; // Destroy old surfaces lpDDSVB->Release(); lpDDS1VB->Release(); // And use new ones lpDDSVB = lpSurface7; lpDDS1VB = lpSurface; position.lpvData = lpMemory; success: this->dwCaps |= D3DVBCAPS_OPTIMIZED; #ifdef VTABLE_HACK // Disable all fast path optimizations VtblLockDefault(); if (this->lpDevIBatched) { this->lpDevIBatched->VtblDrawPrimitiveVBDefault(); this->lpDevIBatched->VtblDrawIndexedPrimitiveVBDefault(); } #endif return D3D_OK; } } #ifdef VTABLE_HACK //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::DrawPrimitiveVBTL" HRESULT D3DAPI CDirect3DDeviceIDP2::DrawPrimitiveVBTL(D3DPRIMITIVETYPE dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, DWORD dwFlags) { LPDIRECT3DVERTEXBUFFERI lpVBufI = static_cast(lpVBuf); #if DBG HRESULT ret = CheckDrawPrimitiveVB(lpVBuf, dwStartVertex, dwNumVertices, dwFlags); if (ret != D3D_OK) return ret; Profile(PROF_DRAWPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf); #endif if ((lpVBufI == lpDP2CurrBatchVBI) && (this->dwVIDIn)) { this->primType = dptPrimitiveType; this->dwNumVertices = dwNumVertices; this->dwFlags = dwFlags | lpVBufI->dwPVFlags; this->dwVertexBase = dwStartVertex; GetNumPrim(this, dwNumVertices); // Calculate dwNumPrimitives #if DBG if (dwNumPrimitives > MAX_DX6_PRIMCOUNT) { D3D_ERR("D3D for DX7 cannot handle greater that 64K sized primitives"); return D3DERR_TOOMANYPRIMITIVES; } #endif this->dp2data.dwFlags &= ~D3DHALDP2_SWAPVERTEXBUFFER; this->dwDP2VertexCount = max(this->dwDP2VertexCount, this->dwVertexBase + this->dwNumVertices); lpVBufI->lpDevIBatched = this; return DrawPrim(); } VtblDrawPrimitiveVBDefault(); return DrawPrimitiveVB(dptPrimitiveType, lpVBuf, dwStartVertex, dwNumVertices, dwFlags); } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::DrawIndexedPrimitiveVBTL" HRESULT D3DAPI CDirect3DDeviceIDP2::DrawIndexedPrimitiveVBTL(D3DPRIMITIVETYPE dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, LPWORD lpwIndices, DWORD dwIndexCount, DWORD dwFlags) { LPDIRECT3DVERTEXBUFFERI lpVBufI = static_cast(lpVBuf); #if DBG HRESULT ret = CheckDrawPrimitiveVB(lpVBuf, dwStartVertex, dwNumVertices, dwFlags); if (ret != D3D_OK) return ret; Profile(PROF_DRAWINDEXEDPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf); #endif if ((lpVBufI == lpDP2CurrBatchVBI) && (this->dwVIDIn)) { this->primType = dptPrimitiveType; this->dwNumVertices = dwNumVertices; this->dwFlags = dwFlags | lpVBufI->dwPVFlags; this->dwVertexBase = dwStartVertex; this->dwNumIndices = dwIndexCount; this->lpwIndices = lpwIndices; GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives #if DBG if (dwNumPrimitives > MAX_DX6_PRIMCOUNT) { D3D_ERR("D3D for DX7 cannot handle greater that 64K sized primitives"); return D3DERR_TOOMANYPRIMITIVES; } #endif this->dp2data.dwFlags &= ~D3DHALDP2_SWAPVERTEXBUFFER; this->dwDP2VertexCount = max(this->dwDP2VertexCount, this->dwVertexBase + this->dwNumVertices); lpVBufI->lpDevIBatched = this; return DrawIndexPrim(); } VtblDrawIndexedPrimitiveVBDefault(); return DrawIndexedPrimitiveVB(dptPrimitiveType, lpVBuf, dwStartVertex, dwNumVertices, lpwIndices, dwIndexCount, dwFlags); } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::DrawPrimitiveVBFE" HRESULT D3DAPI CDirect3DDeviceIDP2::DrawPrimitiveVBFE(D3DPRIMITIVETYPE dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, DWORD dwFlags) { LPDIRECT3DVERTEXBUFFERI lpVBufI = static_cast(lpVBuf); HRESULT ret; #if DBG ret = CheckDrawPrimitiveVB(lpVBuf, dwStartVertex, dwNumVertices, dwFlags); if (ret != D3D_OK) return ret; Profile(PROF_DRAWPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf); #endif if ((lpVBufI == lpDP2LastVBI) && !(this->dwFEFlags & D3DFE_FRONTEND_DIRTY)) { this->primType = dptPrimitiveType; this->dwNumVertices = dwNumVertices; this->dwFlags = this->dwLastFlags | dwFlags | lpVBufI->dwPVFlags; this->position.lpvData = (BYTE*)(lpVBufI->position.lpvData) + dwStartVertex * lpVBufI->position.dwStride; #if DBG GetNumPrim(this, dwNumVertices); // Calculate dwNumPrimitives if (dwNumPrimitives > MAX_DX6_PRIMCOUNT) { D3D_ERR("D3D for DX7 cannot handle greater that 64K sized primitives"); return D3DERR_TOOMANYPRIMITIVES; } #endif this->dwVertexPoolSize = dwNumVertices * this->dwOutputSize; if (this->dwVertexPoolSize > this->TLVbuf_GetSize()) { // try // { if (this->TLVbuf_Grow(this->dwVertexPoolSize, true) != D3D_OK) { D3D_ERR( "Could not grow TL vertex buffer" ); return DDERR_OUTOFMEMORY; } // } // catch (HRESULT ret) // { // return ret; // } } if (dwNumVertices * sizeof(D3DFE_CLIPCODE) > this->HVbuf.GetSize()) { if (this->HVbuf.Grow(dwNumVertices * sizeof(D3DFE_CLIPCODE)) != D3D_OK) { D3D_ERR( "Could not grow clip buffer" ); ret = DDERR_OUTOFMEMORY; return ret; } this->lpClipFlags = (D3DFE_CLIPCODE*)this->HVbuf.GetAddress(); } DDASSERT(this->dwDP2VertexCount * this->dwOutputSize == this->TLVbuf_Base()); this->dwVertexBase = this->dwDP2VertexCount; DDASSERT(this->dwVertexBase < MAX_DX6_VERTICES); dp2data.dwFlags |= D3DHALDP2_SWAPVERTEXBUFFER; this->dwDP2VertexCount = this->dwVertexBase + this->dwNumVertices; this->lpvOut = this->TLVbuf_GetAddress(); // try // { switch (this->primType) { case D3DPT_POINTLIST: this->dwNumPrimitives = dwNumVertices; ret = this->pGeometryFuncs->ProcessPrimitive(this); break; case D3DPT_LINELIST: this->dwNumPrimitives = dwNumVertices >> 1; ret = this->pGeometryFuncs->ProcessPrimitive(this); break; case D3DPT_LINESTRIP: this->dwNumPrimitives = dwNumVertices - 1; ret = this->pGeometryFuncs->ProcessPrimitive(this); break; case D3DPT_TRIANGLEFAN: this->dwNumPrimitives = dwNumVertices - 2; ret = this->pGeometryFuncs->ProcessTriangleFan(this); break; case D3DPT_TRIANGLESTRIP: this->dwNumPrimitives = dwNumVertices - 2; ret = this->pGeometryFuncs->ProcessTriangleStrip(this); break; case D3DPT_TRIANGLELIST: #ifdef _X86_ { DWORD tmp; __asm { mov eax, 0x55555555 // fractional part of 1.0/3.0 mul dwNumVertices add eax, 0x80000000 // Rounding adc edx, 0 mov tmp, edx } this->dwNumPrimitives = tmp; } #else this->dwNumPrimitives = dwNumVertices / 3; #endif ret = this->pGeometryFuncs->ProcessTriangleList(this); break; } // } // catch (HRESULT ret) // { // return ret; // } D3DFE_UpdateClipStatus(this); this->TLVbuf_Base() += this->dwVertexPoolSize; DDASSERT(TLVbuf_base <= TLVbuf_size); DDASSERT(TLVbuf_base == this->dwDP2VertexCount * this->dwOutputSize); return ret; } VtblDrawPrimitiveVBDefault(); return DrawPrimitiveVB(dptPrimitiveType, lpVBuf, dwStartVertex, dwNumVertices, dwFlags); } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::DrawIndexedPrimitiveVBFE" HRESULT D3DAPI CDirect3DDeviceIDP2::DrawIndexedPrimitiveVBFE(D3DPRIMITIVETYPE dptPrimitiveType, LPDIRECT3DVERTEXBUFFER7 lpVBuf, DWORD dwStartVertex, DWORD dwNumVertices, LPWORD lpwIndices, DWORD dwIndexCount, DWORD dwFlags) { LPDIRECT3DVERTEXBUFFERI lpVBufI = static_cast(lpVBuf); HRESULT ret; #if DBG ret = CheckDrawPrimitiveVB(lpVBuf, dwStartVertex, dwNumVertices, dwFlags); if (ret != D3D_OK) return ret; Profile(PROF_DRAWINDEXEDPRIMITIVEVB,dptPrimitiveType,lpVBufI->fvf); #endif if ((lpVBufI == lpDP2LastVBI) && !(this->dwFEFlags & D3DFE_FRONTEND_DIRTY)) { this->primType = dptPrimitiveType; this->dwNumVertices = dwNumVertices; this->dwFlags = this->dwLastFlags | dwFlags | lpVBufI->dwPVFlags; this->dwVertexBase = 0; this->dwNumIndices = dwIndexCount; this->lpwIndices = lpwIndices; GetNumPrim(this, dwNumIndices); // Calculate dwNumPrimitives this->position.lpvData = (BYTE*)(lpVBufI->position.lpvData) + dwStartVertex * lpVBufI->position.dwStride; #if DBG if (dwNumPrimitives > MAX_DX6_PRIMCOUNT) { D3D_ERR("D3D for DX7 cannot handle greater that 64K sized primitives"); return D3DERR_TOOMANYPRIMITIVES; } #endif this->dwVertexPoolSize = dwNumVertices * this->dwOutputSize; if (this->dwVertexPoolSize > this->TLVbuf_GetSize()) { // try // { if (this->TLVbuf_Grow(this->dwVertexPoolSize, (this->dwDeviceFlags & D3DDEV_DONOTCLIP)!=0) != D3D_OK) { D3D_ERR( "Could not grow TL vertex buffer" ); return DDERR_OUTOFMEMORY; } // } // catch (HRESULT ret) // { // return ret; // } } if (dwNumVertices * sizeof(D3DFE_CLIPCODE) > this->HVbuf.GetSize()) { if (this->HVbuf.Grow(dwNumVertices * sizeof(D3DFE_CLIPCODE)) != D3D_OK) { D3D_ERR( "Could not grow clip buffer" ); ret = DDERR_OUTOFMEMORY; return ret; } this->lpClipFlags = (D3DFE_CLIPCODE*)this->HVbuf.GetAddress(); } this->dwVertexBase = this->dwDP2VertexCount; DDASSERT(this->dwVertexBase < MAX_DX6_VERTICES); dp2data.dwFlags |= D3DHALDP2_SWAPVERTEXBUFFER; this->dwDP2VertexCount = this->dwVertexBase + this->dwNumVertices; this->lpvOut = this->TLVbuf_GetAddress(); // try // { ret = this->pGeometryFuncs->ProcessIndexedPrimitive(this); // } // catch (HRESULT ret) // { // return ret; // } D3DFE_UpdateClipStatus(this); this->TLVbuf_Base() += this->dwVertexPoolSize; DDASSERT(TLVbuf_base <= TLVbuf_size); DDASSERT(TLVbuf_base == this->dwDP2VertexCount * this->dwOutputSize); return ret; } VtblDrawIndexedPrimitiveVBDefault(); return DrawIndexedPrimitiveVB(dptPrimitiveType, lpVBuf, dwStartVertex, dwNumVertices, lpwIndices, dwIndexCount, dwFlags); } #endif // VTABLE_HACK