/* ** Command & Conquer Renegade(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 O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : ww3d * * * * $Archive:: /Commando/Code/ww3d2/dx8indexbuffer.cpp $* * * * Original Author:: Jani Penttinen * * * * $Author:: Jani_p $* * * * $Modtime:: 11/09/01 3:12p $* * * * $Revision:: 26 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ //#define INDEX_BUFFER_LOG #include "dx8indexbuffer.h" #include "dx8wrapper.h" #include "dx8caps.h" #include "sphere.h" #include "thread.h" #include "wwmemlog.h" #define DEFAULT_IB_SIZE 5000 static bool _DynamicSortingIndexArrayInUse=false; static SortingIndexBufferClass* _DynamicSortingIndexArray; static unsigned short _DynamicSortingIndexArraySize=0; static unsigned short _DynamicSortingIndexArrayOffset=0; static bool _DynamicDX8IndexBufferInUse=false; static DX8IndexBufferClass* _DynamicDX8IndexBuffer=NULL; static unsigned short _DynamicDX8IndexBufferSize=DEFAULT_IB_SIZE; static unsigned short _DynamicDX8IndexBufferOffset=0; static int _IndexBufferCount; static int _IndexBufferTotalIndices; static int _IndexBufferTotalSize; // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- IndexBufferClass::IndexBufferClass(unsigned type_, unsigned short index_count_) : index_count(index_count_), type(type_), engine_refs(0) { WWASSERT(type==BUFFER_TYPE_DX8 || type==BUFFER_TYPE_SORTING); WWASSERT(index_count); _IndexBufferCount++; _IndexBufferTotalIndices+=index_count; _IndexBufferTotalSize+=index_count*sizeof(unsigned short); #ifdef VERTEX_BUFFER_LOG WWDEBUG_SAY(("New IB, %d indices, size %d bytes\n",index_count,index_count*sizeof(unsigned short))); WWDEBUG_SAY(("Total IB count: %d, total %d indices, total size %d bytes\n", _IndexBufferCount, _IndexBufferTotalIndices, _IndexBufferTotalSize)); #endif } IndexBufferClass::~IndexBufferClass() { _IndexBufferCount--; _IndexBufferTotalIndices-=index_count; _IndexBufferTotalSize-=index_count*sizeof(unsigned short); #ifdef VERTEX_BUFFER_LOG WWDEBUG_SAY(("Delete IB, %d indices, size %d bytes\n",index_count,index_count*sizeof(unsigned short))); WWDEBUG_SAY(("Total IB count: %d, total %d indices, total size %d bytes\n", _IndexBufferCount, _IndexBufferTotalIndices, _IndexBufferTotalSize)); #endif } unsigned IndexBufferClass::Get_Total_Buffer_Count() { return _IndexBufferCount; } unsigned IndexBufferClass::Get_Total_Allocated_Indices() { return _IndexBufferTotalIndices; } unsigned IndexBufferClass::Get_Total_Allocated_Memory() { return _IndexBufferTotalSize; } void IndexBufferClass::Add_Engine_Ref() const { engine_refs++; } void IndexBufferClass::Release_Engine_Ref() const { engine_refs--; WWASSERT(engine_refs>=0); } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- void IndexBufferClass::Copy(unsigned int* indices,unsigned first_index,unsigned count) { WWASSERT(indices); if (first_index) { DX8IndexBufferClass::AppendLockClass l(this,first_index,count); unsigned short* inds=l.Get_Index_Array(); for (unsigned v=0;vEngine_Refs()); index_buffer->Add_Ref(); switch (index_buffer->Type()) { case BUFFER_TYPE_DX8: DX8_Assert(); DX8_ErrorCode(static_cast(index_buffer)->Get_DX8_Index_Buffer()->Lock( 0, index_buffer->Get_Index_Count()*sizeof(WORD), (unsigned char**)&indices, 0)); break; case BUFFER_TYPE_SORTING: indices=static_cast(index_buffer)->index_buffer; break; default: WWASSERT(0); break; } } // ---------------------------------------------------------------------------- // // // ---------------------------------------------------------------------------- IndexBufferClass::WriteLockClass::~WriteLockClass() { DX8_THREAD_ASSERT(); switch (index_buffer->Type()) { case BUFFER_TYPE_DX8: DX8_Assert(); DX8_ErrorCode(static_cast(index_buffer)->index_buffer->Unlock()); break; case BUFFER_TYPE_SORTING: break; default: WWASSERT(0); break; } index_buffer->Release_Ref(); } // ---------------------------------------------------------------------------- IndexBufferClass::AppendLockClass::AppendLockClass(IndexBufferClass* index_buffer_,unsigned start_index, unsigned index_range) : index_buffer(index_buffer_) { DX8_THREAD_ASSERT(); WWASSERT(start_index+index_range<=index_buffer->Get_Index_Count()); WWASSERT(index_buffer); WWASSERT(!index_buffer->Engine_Refs()); index_buffer->Add_Ref(); switch (index_buffer->Type()) { case BUFFER_TYPE_DX8: DX8_Assert(); DX8_ErrorCode(static_cast(index_buffer)->index_buffer->Lock( start_index*sizeof(unsigned short), index_range*sizeof(unsigned short), (unsigned char**)&indices, NULL)); // Optional pointer to receive the buffer size break; case BUFFER_TYPE_SORTING: indices=static_cast(index_buffer)->index_buffer+start_index; break; default: WWASSERT(0); break; } } // ---------------------------------------------------------------------------- IndexBufferClass::AppendLockClass::~AppendLockClass() { DX8_THREAD_ASSERT(); switch (index_buffer->Type()) { case BUFFER_TYPE_DX8: DX8_Assert(); DX8_ErrorCode(static_cast(index_buffer)->index_buffer->Unlock()); break; case BUFFER_TYPE_SORTING: break; default: WWASSERT(0); break; } index_buffer->Release_Ref(); } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- DX8IndexBufferClass::DX8IndexBufferClass(unsigned short index_count_,UsageType usage) : IndexBufferClass(BUFFER_TYPE_DX8,index_count_) { DX8_THREAD_ASSERT(); WWASSERT(index_count); unsigned usage_flags= D3DUSAGE_WRITEONLY| ((usage&USAGE_DYNAMIC) ? D3DUSAGE_DYNAMIC : 0)| ((usage&USAGE_NPATCHES) ? D3DUSAGE_NPATCHES : 0)| ((usage&USAGE_SOFTWAREPROCESSING) ? D3DUSAGE_SOFTWAREPROCESSING : 0); if (!DX8Wrapper::Get_Current_Caps()->Support_TnL()) { usage_flags|=D3DUSAGE_SOFTWAREPROCESSING; } HRESULT ret=DX8Wrapper::_Get_D3D_Device8()->CreateIndexBuffer( sizeof(WORD)*index_count, usage_flags, D3DFMT_INDEX16, (usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED, &index_buffer); if (SUCCEEDED(ret)) { return; } WWDEBUG_SAY(("Index buffer creation failed, trying to release assets...\n")); // Vertex buffer creation failed, so try releasing least used textures and flushing the mesh cache. // Free all textures that haven't been used in the last 5 seconds TextureClass::Invalidate_Old_Unused_Textures(5000); // Invalidate the mesh cache WW3D::_Invalidate_Mesh_Cache(); // Try again... ret=DX8Wrapper::_Get_D3D_Device8()->CreateIndexBuffer( sizeof(WORD)*index_count, usage_flags, D3DFMT_INDEX16, (usage&USAGE_DYNAMIC) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED, &index_buffer); if (SUCCEEDED(ret)) { WWDEBUG_SAY(("...Index buffer creation succesful\n")); } // If it still fails it is fatal DX8_ErrorCode(ret); } // ---------------------------------------------------------------------------- DX8IndexBufferClass::~DX8IndexBufferClass() { index_buffer->Release(); } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- SortingIndexBufferClass::SortingIndexBufferClass(unsigned short index_count_) : IndexBufferClass(BUFFER_TYPE_SORTING,index_count_) { WWMEMLOG(MEM_RENDERER); WWASSERT(index_count); index_buffer=new unsigned short[index_count]; } // ---------------------------------------------------------------------------- SortingIndexBufferClass::~SortingIndexBufferClass() { delete[] index_buffer; } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- DynamicIBAccessClass::DynamicIBAccessClass(unsigned short type_, unsigned short index_count_) : IndexCount(index_count_), IndexBuffer(0), Type(type_) { WWASSERT(Type==BUFFER_TYPE_DYNAMIC_DX8 || Type==BUFFER_TYPE_DYNAMIC_SORTING); if (Type==BUFFER_TYPE_DYNAMIC_DX8) { Allocate_DX8_Dynamic_Buffer(); } else { Allocate_Sorting_Dynamic_Buffer(); } } DynamicIBAccessClass::~DynamicIBAccessClass() { REF_PTR_RELEASE(IndexBuffer); if (Type==BUFFER_TYPE_DYNAMIC_DX8) { _DynamicDX8IndexBufferInUse=false; _DynamicDX8IndexBufferOffset+=IndexCount; } else { _DynamicSortingIndexArrayInUse=false; _DynamicSortingIndexArrayOffset+=IndexCount; } } void DynamicIBAccessClass::_Deinit() { WWASSERT ((_DynamicDX8IndexBuffer == NULL) || (_DynamicDX8IndexBuffer->Num_Refs() == 1)); REF_PTR_RELEASE(_DynamicDX8IndexBuffer); _DynamicDX8IndexBufferInUse=false; _DynamicDX8IndexBufferSize=DEFAULT_IB_SIZE; _DynamicDX8IndexBufferOffset=0; WWASSERT ((_DynamicSortingIndexArray == NULL) || (_DynamicSortingIndexArray->Num_Refs() == 1)); REF_PTR_RELEASE(_DynamicSortingIndexArray); _DynamicSortingIndexArrayInUse=false; _DynamicSortingIndexArraySize=0; _DynamicSortingIndexArrayOffset=0; } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- DynamicIBAccessClass::WriteLockClass::WriteLockClass(DynamicIBAccessClass* ib_access_) : DynamicIBAccess(ib_access_) { DX8_THREAD_ASSERT(); DynamicIBAccess->IndexBuffer->Add_Ref(); switch (DynamicIBAccess->Get_Type()) { case BUFFER_TYPE_DYNAMIC_DX8: WWASSERT(DynamicIBAccess); // WWASSERT(!dynamic_dx8_index_buffer->Engine_Refs()); DX8_Assert(); DX8_ErrorCode( static_cast(DynamicIBAccess->IndexBuffer)->Get_DX8_Index_Buffer()->Lock( DynamicIBAccess->IndexBufferOffset*sizeof(WORD), DynamicIBAccess->Get_Index_Count()*sizeof(WORD), (unsigned char**)&Indices, !DynamicIBAccess->IndexBufferOffset ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE)); break; case BUFFER_TYPE_DYNAMIC_SORTING: Indices=static_cast(DynamicIBAccess->IndexBuffer)->index_buffer; Indices+=DynamicIBAccess->IndexBufferOffset; break; default: WWASSERT(0); break; } } DynamicIBAccessClass::WriteLockClass::~WriteLockClass() { DX8_THREAD_ASSERT(); switch (DynamicIBAccess->Get_Type()) { case BUFFER_TYPE_DYNAMIC_DX8: DX8_Assert(); DX8_ErrorCode(static_cast(DynamicIBAccess->IndexBuffer)->Get_DX8_Index_Buffer()->Unlock()); break; case BUFFER_TYPE_DYNAMIC_SORTING: break; default: WWASSERT(0); break; } DynamicIBAccess->IndexBuffer->Release_Ref(); } // ---------------------------------------------------------------------------- // // // // ---------------------------------------------------------------------------- void DynamicIBAccessClass::Allocate_DX8_Dynamic_Buffer() { WWMEMLOG(MEM_RENDERER); WWASSERT(!_DynamicDX8IndexBufferInUse); _DynamicDX8IndexBufferInUse=true; // If requesting more indices than dynamic index buffer can fit, delete the ib // and adjust the size to the new count. if (IndexCount>_DynamicDX8IndexBufferSize) { REF_PTR_RELEASE(_DynamicDX8IndexBuffer); _DynamicDX8IndexBufferSize=IndexCount; if (_DynamicDX8IndexBufferSizeSupport_NPatches()) { usage|=DX8IndexBufferClass::USAGE_NPATCHES; } _DynamicDX8IndexBuffer=NEW_REF(DX8IndexBufferClass,( _DynamicDX8IndexBufferSize, (DX8IndexBufferClass::UsageType)usage)); _DynamicDX8IndexBufferOffset=0; } // Any room at the end of the buffer? if (((unsigned)IndexCount+_DynamicDX8IndexBufferOffset)>_DynamicDX8IndexBufferSize) { _DynamicDX8IndexBufferOffset=0; } REF_PTR_SET(IndexBuffer,_DynamicDX8IndexBuffer); IndexBufferOffset=_DynamicDX8IndexBufferOffset; } void DynamicIBAccessClass::Allocate_Sorting_Dynamic_Buffer() { WWMEMLOG(MEM_RENDERER); WWASSERT(!_DynamicSortingIndexArrayInUse); _DynamicSortingIndexArrayInUse=true; unsigned new_index_count=_DynamicSortingIndexArrayOffset+IndexCount; WWASSERT(new_index_count<65536); if (new_index_count>_DynamicSortingIndexArraySize) { REF_PTR_RELEASE(_DynamicSortingIndexArray); _DynamicSortingIndexArraySize=new_index_count; if (_DynamicSortingIndexArraySize