/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// #include "Common/Debug.h" #include "W3DDevice/GameClient/W3DBufferManager.h" W3DBufferManager *TheW3DBufferManager=NULL; //singleton static int FVFTypeIndexList[W3DBufferManager::MAX_FVF]= { D3DFVF_XYZ, D3DFVF_XYZ|D3DFVF_DIFFUSE, D3DFVF_XYZ|D3DFVF_TEX1, D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1, D3DFVF_XYZ|D3DFVF_TEX2, D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX2, D3DFVF_XYZ|D3DFVF_NORMAL, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX2, D3DFVF_XYZRHW, D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DFVF_XYZRHW|D3DFVF_TEX1, D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1, D3DFVF_XYZRHW|D3DFVF_TEX2, D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX2 }; Int W3DBufferManager::getDX8Format(VBM_FVF_TYPES format) { return FVFTypeIndexList[format]; } W3DBufferManager::W3DBufferManager(void) { m_numEmptySlotsAllocated=0; m_numEmptyVertexBuffersAllocated=0; m_numEmptyIndexSlotsAllocated=0; m_numEmptyIndexBuffersAllocated=0; for (Int i=0; im_prevSameVB) vbSlot->m_prevSameVB->m_nextSameVB=vbSlot->m_nextSameVB; else vbSlot->m_VB->m_usedSlots=NULL; if (vbSlot->m_nextSameVB) vbSlot->m_nextSameVB->m_prevSameVB=vbSlot->m_prevSameVB; vbSlot=vbSlot->m_nextSameSize; m_numEmptySlotsAllocated--; } m_W3DVertexBufferSlots[i][j]=NULL; } } for (j=0; jm_prevSameIB) ibSlot->m_prevSameIB->m_nextSameIB=ibSlot->m_nextSameIB; else ibSlot->m_IB->m_usedSlots=NULL; if (ibSlot->m_nextSameIB) ibSlot->m_nextSameIB->m_prevSameIB=ibSlot->m_prevSameIB; ibSlot=ibSlot->m_nextSameSize; m_numEmptyIndexSlotsAllocated--; } m_W3DIndexBufferSlots[j]=NULL; } DEBUG_ASSERTCRASH(m_numEmptySlotsAllocated==0, ("Failed to free all empty vertex buffer slots")); DEBUG_ASSERTCRASH(m_numEmptyIndexSlotsAllocated==0, ("Failed to free all empty index buffer slots")); } void W3DBufferManager::freeAllBuffers(void) { Int i; //Make sure all slots are free freeAllSlots(); ///m_usedSlots == NULL, ("Freeing Non-Empty Vertex Buffer")); REF_PTR_RELEASE(vb->m_DX8VertexBuffer); m_numEmptyVertexBuffersAllocated--; vb=vb->m_nextVB; //get next vertex buffer of this type } m_W3DVertexBuffers[i]=NULL; } W3DIndexBuffer *ib = m_W3DIndexBuffers; while (ib) { DEBUG_ASSERTCRASH(ib->m_usedSlots == NULL, ("Freeing Non-Empty Index Buffer")); REF_PTR_RELEASE(ib->m_DX8IndexBuffer); m_numEmptyIndexBuffersAllocated--; ib=ib->m_nextIB; //get next vertex buffer of this type } m_W3DIndexBuffers=NULL; DEBUG_ASSERTCRASH(m_numEmptyVertexBuffersAllocated==0, ("Failed to free all empty vertex buffers")); DEBUG_ASSERTCRASH(m_numEmptyIndexBuffersAllocated==0, ("Failed to free all empty index buffers")); } void W3DBufferManager::ReleaseResources(void) { for (Int i=0; im_DX8VertexBuffer); vb=vb->m_nextVB; //get next vertex buffer of this type } } W3DIndexBuffer *ib = m_W3DIndexBuffers; while (ib) { REF_PTR_RELEASE(ib->m_DX8IndexBuffer); ib=ib->m_nextIB; //get next vertex buffer of this type } } Bool W3DBufferManager::ReAcquireResources(void) { for (Int i=0; im_DX8VertexBuffer == NULL, ("ReAcquire of existing vertex buffer")); vb->m_DX8VertexBuffer=NEW_REF(DX8VertexBufferClass,(FVFTypeIndexList[vb->m_format],vb->m_size,DX8VertexBufferClass::USAGE_DEFAULT)); DEBUG_ASSERTCRASH( vb->m_DX8VertexBuffer, ("Failed ReAcquire of vertex buffer")); if (!vb->m_DX8VertexBuffer) return FALSE; vb=vb->m_nextVB; //get next vertex buffer of this type } } W3DIndexBuffer *ib = m_W3DIndexBuffers; while (ib) { DEBUG_ASSERTCRASH( ib->m_DX8IndexBuffer == NULL, ("ReAcquire of existing index buffer")); ib->m_DX8IndexBuffer=NEW_REF(DX8IndexBufferClass,(ib->m_size,DX8IndexBufferClass::USAGE_DEFAULT)); DEBUG_ASSERTCRASH( ib->m_DX8IndexBuffer, ("Failed ReAcquire of index buffer")); if (!ib->m_DX8IndexBuffer) return FALSE; ib=ib->m_nextIB; //get next vertex buffer of this type } return TRUE; } /**Searches through previously allocated vertex buffer slots and returns a matching type. If none found, creates a new slot and adds it to the pool. Returns an integer slotId used to reference the VB. Returns -1 in case of failure. */ W3DBufferManager::W3DVertexBufferSlot *W3DBufferManager::getSlot(VBM_FVF_TYPES fvfType, Int size) { W3DVertexBufferSlot *vbSlot=NULL; //round size to next multiple of minimum slot size. //should help avoid fragmentation. size = (size + (MIN_SLOT_SIZE-1)) & (~(MIN_SLOT_SIZE-1)); Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1; DEBUG_ASSERTCRASH(sizeIndex < MAX_VB_SIZES && size, ("Allocating too large vertex buffer slot")); if ((vbSlot=m_W3DVertexBufferSlots[fvfType][sizeIndex]) != 0) { //found a previously allocated slot matching required size m_W3DVertexBufferSlots[fvfType][sizeIndex]=vbSlot->m_nextSameSize; if (vbSlot->m_nextSameSize) vbSlot->m_nextSameSize->m_prevSameSize=NULL; return vbSlot; } else { //need to allocate a new slot return allocateSlotStorage(fvfType, size); } return NULL; } /**Returns vertex buffer space back to pool so it can be reused later*/ void W3DBufferManager::releaseSlot(W3DVertexBufferSlot *vbSlot) { Int sizeIndex = (vbSlot->m_size >> MIN_SLOT_SIZE_SHIFT)-1; vbSlot->m_nextSameSize=m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex]; if (m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex]) m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex]->m_prevSameSize=vbSlot; m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex]=vbSlot; } /**Reserves space inside existing vertex buffer or allocates a new one to fit the required size. */ W3DBufferManager::W3DVertexBufferSlot * W3DBufferManager::allocateSlotStorage(VBM_FVF_TYPES fvfType, Int size) { W3DVertexBuffer *pVB; W3DVertexBufferSlot *vbSlot; // Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1; DEBUG_ASSERTCRASH(m_numEmptySlotsAllocated < MAX_NUMBER_SLOTS, ("Nore more VB Slots")); pVB=m_W3DVertexBuffers[fvfType]; while (pVB) { if ((pVB->m_size - pVB->m_startFreeIndex) >= size) { //found enough free space in this vertex buffer if (m_numEmptySlotsAllocated < MAX_NUMBER_SLOTS) { //we're allowing more slots to be allocated. vbSlot=&m_W3DVertexBufferEmptySlots[m_numEmptySlotsAllocated]; vbSlot->m_size=size; vbSlot->m_start=pVB->m_startFreeIndex; vbSlot->m_VB=pVB; //Link to VB list of slots vbSlot->m_nextSameVB=pVB->m_usedSlots; vbSlot->m_prevSameVB=NULL; //this will be the new head if (pVB->m_usedSlots) pVB->m_usedSlots->m_prevSameVB=vbSlot; vbSlot->m_prevSameSize=vbSlot->m_nextSameSize=NULL; pVB->m_usedSlots=vbSlot; pVB->m_startFreeIndex += size; m_numEmptySlotsAllocated++; return vbSlot; } } pVB = pVB->m_nextVB; } pVB=m_W3DVertexBuffers[fvfType]; //save old list head //Didn't find any vertex buffers with room, create a new one DEBUG_ASSERTCRASH(m_numEmptyVertexBuffersAllocated < MAX_VERTEX_BUFFERS_CREATED, ("Reached Max Static VB Shadow Geometry")); if (m_numEmptyVertexBuffersAllocated < MAX_VERTEX_BUFFERS_CREATED) { m_W3DVertexBuffers[fvfType] = &m_W3DEmptyVertexBuffers[m_numEmptyVertexBuffersAllocated]; m_W3DVertexBuffers[fvfType]->m_nextVB=pVB; //link to list m_numEmptyVertexBuffersAllocated++; pVB=m_W3DVertexBuffers[fvfType]; //get new list head Int vbSize=__max(DEFAULT_VERTEX_BUFFER_SIZE,size); pVB->m_DX8VertexBuffer=NEW_REF(DX8VertexBufferClass,(FVFTypeIndexList[fvfType],vbSize,DX8VertexBufferClass::USAGE_DEFAULT)); pVB->m_format=fvfType; pVB->m_startFreeIndex=size; pVB->m_size=vbSize; vbSlot=&m_W3DVertexBufferEmptySlots[m_numEmptySlotsAllocated]; m_numEmptySlotsAllocated++; pVB->m_usedSlots=vbSlot; vbSlot->m_size=size; vbSlot->m_start=0; vbSlot->m_VB=pVB; vbSlot->m_prevSameVB=vbSlot->m_nextSameVB=NULL; vbSlot->m_prevSameSize=vbSlot->m_nextSameSize=NULL; return vbSlot; } return NULL; } //******************************** Index Buffer code ****************************************************** /**Searches through previously allocated index buffer slots and returns a matching type. If none found, creates a new slot and adds it to the pool. Returns an integer slotId used to reference the VB. Returns -1 in case of failure. */ W3DBufferManager::W3DIndexBufferSlot *W3DBufferManager::getSlot(Int size) { W3DIndexBufferSlot *ibSlot=NULL; //round size to next multiple of minimum slot size. //should help avoid fragmentation. size = (size + (MIN_SLOT_SIZE-1)) & (~(MIN_SLOT_SIZE-1)); Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1; DEBUG_ASSERTCRASH(sizeIndex < MAX_IB_SIZES && size, ("Allocating too large index buffer slot")); if ((ibSlot=m_W3DIndexBufferSlots[sizeIndex]) != 0) { //found a previously allocated slot matching required size m_W3DIndexBufferSlots[sizeIndex]=ibSlot->m_nextSameSize; if (ibSlot->m_nextSameSize) ibSlot->m_nextSameSize->m_prevSameSize=NULL; return ibSlot; } else { //need to allocate a new slot return allocateSlotStorage(size); } return NULL; } /**Returns index buffer space back to pool so it can be reused later*/ void W3DBufferManager::releaseSlot(W3DIndexBufferSlot *ibSlot) { Int sizeIndex = (ibSlot->m_size >> MIN_SLOT_SIZE_SHIFT)-1; ibSlot->m_nextSameSize=m_W3DIndexBufferSlots[sizeIndex]; if (m_W3DIndexBufferSlots[sizeIndex]) m_W3DIndexBufferSlots[sizeIndex]->m_prevSameSize=ibSlot; m_W3DIndexBufferSlots[sizeIndex]=ibSlot; } /**Reserves space inside existing index buffer or allocates a new one to fit the required size. */ W3DBufferManager::W3DIndexBufferSlot * W3DBufferManager::allocateSlotStorage(Int size) { W3DIndexBuffer *pIB; W3DIndexBufferSlot *ibSlot; // Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1; DEBUG_ASSERTCRASH(m_numEmptyIndexSlotsAllocated < MAX_NUMBER_SLOTS, ("Nore more IB Slots")); pIB=m_W3DIndexBuffers; while (pIB) { if ((pIB->m_size - pIB->m_startFreeIndex) >= size) { //found enough free space in this index buffer if (m_numEmptyIndexSlotsAllocated < MAX_NUMBER_SLOTS) { //we're allowing more slots to be allocated. ibSlot=&m_W3DIndexBufferEmptySlots[m_numEmptyIndexSlotsAllocated]; ibSlot->m_size=size; ibSlot->m_start=pIB->m_startFreeIndex; ibSlot->m_IB=pIB; //Link to IB list of slots ibSlot->m_nextSameIB=pIB->m_usedSlots; ibSlot->m_prevSameIB=NULL; //this will be the new head if (pIB->m_usedSlots) pIB->m_usedSlots->m_prevSameIB=ibSlot; ibSlot->m_prevSameSize=ibSlot->m_nextSameSize=NULL; pIB->m_usedSlots=ibSlot; pIB->m_startFreeIndex += size; m_numEmptyIndexSlotsAllocated++; return ibSlot; } } pIB = pIB->m_nextIB; } pIB=m_W3DIndexBuffers; //save old list head //Didn't find any index buffers with room, create a new one DEBUG_ASSERTCRASH(m_numEmptyIndexBuffersAllocated < MAX_INDEX_BUFFERS_CREATED, ("Reached Max Static IB Shadow Geometry")); if (m_numEmptyIndexBuffersAllocated < MAX_INDEX_BUFFERS_CREATED) { m_W3DIndexBuffers = &m_W3DEmptyIndexBuffers[m_numEmptyIndexBuffersAllocated]; m_W3DIndexBuffers->m_nextIB=pIB; //link to list m_numEmptyIndexBuffersAllocated++; pIB=m_W3DIndexBuffers; //get new list head Int ibSize=__max(DEFAULT_INDEX_BUFFER_SIZE,size); pIB->m_DX8IndexBuffer=NEW_REF(DX8IndexBufferClass,(ibSize,DX8IndexBufferClass::USAGE_DEFAULT)); pIB->m_startFreeIndex=size; pIB->m_size=ibSize; ibSlot=&m_W3DIndexBufferEmptySlots[m_numEmptyIndexSlotsAllocated]; m_numEmptyIndexSlotsAllocated++; pIB->m_usedSlots=ibSlot; ibSlot->m_size=size; ibSlot->m_start=0; ibSlot->m_IB=pIB; ibSlot->m_prevSameIB=ibSlot->m_nextSameIB=NULL; ibSlot->m_prevSameSize=ibSlot->m_nextSameSize=NULL; return ibSlot; } return NULL; }