//==== Copyright © 1996-2008, Valve Corporation, All rights reserved. =======// // // Purpose: // //===========================================================================// #ifndef INDEXDATA_H #define INDEXDATA_H #ifdef _WIN32 #pragma once #endif #include "tier0/platform.h" #include "rendersystem/irendercontext.h" //----------------------------------------------------------------------------- // // Helper class used to define index buffers // //----------------------------------------------------------------------------- template < class T > class CIndexData { public: CIndexData( IRenderContext* pRenderContext, HRenderBuffer hIndexBuffer ); CIndexData( IRenderContext* pRenderContext, RenderBufferType_t nType, int nIndexCount, const char *pDebugName, const char *pBudgetGroup ); // This constructor is meant to be used with instance rendering. // Passing in either 0 or INT_MAX here means the client code // doesn't know how many instances will be rendered. CIndexData( IRenderContext* pRenderContext, RenderBufferType_t nType, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ); ~CIndexData(); // Begins, ends modification of the index buffer (returns true if the lock succeeded) // A lock may not succeed if there isn't enough room // Passing in 0 locks the entire buffer bool Lock( int nMaxIndexCount = 0 ); void Unlock(); // returns the number of indices int IndexCount() const; // returns the total # of indices in the entire buffer int GetBufferIndexCount() const; // Used to define the indices (only used if you aren't using primitives) void Index( T nIndex ); // NOTE: This version is the one you really want to achieve write-combining; // Write combining only works if you write in 4 bytes chunks. void Index2( T nIndex1, T nIndex2 ); /* void FastTriangle( T nStartVert ); void FastQuad( T nStartVert ); void FastPolygon( T nStartVert, int nEdgeCount ); void FastPolygonList( T nStartVert, int *pVertexCount, int polygonCount ); void FastIndexList( const T *pIndexList, T nStartVert, int indexCount ); */ // Call this to detach ownership of the vertex buffer. Caller is responsible // for deleting it now HRenderBuffer TakeOwnership(); protected: enum { BUFFER_OFFSET_UNINITIALIZED = 0xFFFFFFFF }; void Init( IRenderContext* pRenderContext, RenderBufferType_t nType, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ); void Release(); // Pointer to the memory we're writing T* m_pMemory; // The current index int m_nIndexCount; // Amount to increase the index count each time (0 if there was a lock failure) int m_nIndexIncrement; // The mesh we're modifying IRenderContext* m_pRenderContext; HRenderBuffer m_hIndexBuffer; // Max number of indices int m_nMaxIndexCount; int m_nBufferIndexCount : 31; int m_bShouldDeallocate : 1; }; //----------------------------------------------------------------------------- // // Inline methods related to CIndexData // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- template< class T > inline CIndexData::CIndexData( IRenderContext* pRenderContext, HRenderBuffer hIndexBuffer ) { m_pRenderContext = pRenderContext; m_hIndexBuffer = hIndexBuffer; m_bShouldDeallocate = false; BufferDesc_t desc; pRenderContext->GetDevice()->GetIndexBufferDesc( hIndexBuffer, &desc ); m_nBufferIndexCount = desc.m_nElementCount; #ifdef _DEBUG m_nIndexCount = 0; m_nMaxIndexCount = 0; m_pMemory = NULL; m_nIndexIncrement = 0; #endif } template< class T > inline CIndexData::CIndexData( IRenderContext* pRenderContext, RenderBufferType_t nType, int nIndexCount, const char *pDebugName, const char *pBudgetGroup ) { Init( pRenderContext, nType, nIndexCount, 0, pDebugName, pBudgetGroup ); } template< class T > inline CIndexData::CIndexData( IRenderContext* pRenderContext, RenderBufferType_t nType, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ) { if ( nMaxInstanceCount == 0 ) { nMaxInstanceCount = INT_MAX; } Init( pRenderContext, nType, nIndexCount, nMaxInstanceCount, pDebugName, pBudgetGroup ); } template< class T > inline void CIndexData::Init( IRenderContext* pRenderContext, RenderBufferType_t nType, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ) { m_pRenderContext = pRenderContext; BufferDesc_t indexDesc; indexDesc.m_nElementSizeInBytes = sizeof(T); indexDesc.m_nElementCount = nIndexCount; indexDesc.m_pDebugName = pDebugName; indexDesc.m_pBudgetGroupName = pBudgetGroup; m_hIndexBuffer = pRenderContext->GetDevice()->CreateIndexBuffer( nType, indexDesc, nMaxInstanceCount ); m_nBufferIndexCount = nIndexCount; m_bShouldDeallocate = true; ResourceAddRef( m_hIndexBuffer ); #ifdef _DEBUG m_nIndexCount = 0; m_nMaxIndexCount = 0; m_pMemory = NULL; m_nIndexIncrement = 0; #endif } template< class T > inline CIndexData::~CIndexData() { // If this assertion fails, you forgot to unlock Assert( !m_pMemory ); Release(); } //----------------------------------------------------------------------------- // Release //----------------------------------------------------------------------------- template< class T > void CIndexData::Release() { if ( m_bShouldDeallocate && ( m_hIndexBuffer != RENDER_BUFFER_HANDLE_INVALID ) ) { ResourceRelease( m_hIndexBuffer ); m_pRenderContext->GetDevice()->DestroyIndexBuffer( m_hIndexBuffer ); m_hIndexBuffer = RENDER_BUFFER_HANDLE_INVALID; m_bShouldDeallocate = false; } } //----------------------------------------------------------------------------- // Call this to take ownership of the vertex buffer //----------------------------------------------------------------------------- template< class T > inline HRenderBuffer CIndexData::TakeOwnership() { if ( m_bShouldDeallocate ) { ResourceRelease( m_hIndexBuffer ); } m_bShouldDeallocate = false; return m_hIndexBuffer; } //----------------------------------------------------------------------------- // Returns the buffer vertex count //----------------------------------------------------------------------------- template< class T > inline int CIndexData::GetBufferIndexCount() const { return m_nBufferIndexCount; } //----------------------------------------------------------------------------- // Begins, ends modification of the index buffer //----------------------------------------------------------------------------- template< class T > inline bool CIndexData::Lock( int nMaxIndexCount ) { if ( nMaxIndexCount == 0 ) { nMaxIndexCount = m_nBufferIndexCount; } // Lock the index buffer LockDesc_t desc; bool bOk = m_pRenderContext->LockIndexBuffer( m_hIndexBuffer, nMaxIndexCount * sizeof(T), &desc ); m_nIndexIncrement = bOk ? 1 : 0; m_nMaxIndexCount = nMaxIndexCount * m_nIndexIncrement; m_nIndexCount = 0; m_pMemory = (T*)desc.m_pMemory; return bOk; } template< class T > inline void CIndexData::Unlock() { LockDesc_t desc; desc.m_pMemory = m_pMemory; m_pRenderContext->UnlockIndexBuffer( m_hIndexBuffer, m_nIndexCount * sizeof(T), &desc ); #ifdef _DEBUG m_nIndexCount = 0; m_nMaxIndexCount = 0; m_pMemory = 0; m_nIndexIncrement = 0; #endif } /* //----------------------------------------------------------------------------- // Binds this index buffer //----------------------------------------------------------------------------- inline void CIndexData::Bind( IMatRenderContext *pContext ) { pContext->BindIndexBuffer( m_pIndexBuffer, 0 ); } */ //----------------------------------------------------------------------------- // returns the number of indices //----------------------------------------------------------------------------- template< class T > inline int CIndexData::IndexCount() const { return m_nIndexCount; } //----------------------------------------------------------------------------- // Used to write data into the index buffer //----------------------------------------------------------------------------- template< class T > inline void CIndexData::Index( T nIndex ) { // FIXME: Should we prevent use of this with T = uint16? (write-combining) Assert( m_pMemory ); Assert( ( m_nIndexIncrement == 0 ) || ( m_nIndexCount < m_nMaxIndexCount ) ); m_pMemory[m_nIndexCount] = nIndex; m_nIndexCount += m_nIndexIncrement; } //----------------------------------------------------------------------------- // NOTE: This version is the one you really want to achieve write-combining; // Write combining only works if you write in 4 bytes chunks. //----------------------------------------------------------------------------- template< > inline void CIndexData::Index2( uint16 nIndex1, uint16 nIndex2 ) { Assert( m_pMemory ); Assert( ( m_nIndexIncrement == 0 ) || ( m_nIndexCount < m_nMaxIndexCount - 1 ) ); #ifndef _X360 uint32 nIndices = ( (uint32)nIndex1 ) | ( ( (uint32)nIndex2 ) << 16 ); #else uint32 nIndices = ( (uint32)nIndex2 ) | ( ( (uint32)nIndex1 ) << 16 ); #endif *(uint32*)( &m_pMemory[m_nIndexCount] ) = nIndices; m_nIndexCount += m_nIndexIncrement + m_nIndexIncrement; } template< > inline void CIndexData::Index2( uint32 nIndex1, uint32 nIndex2 ) { Assert( m_pMemory ); Assert( ( m_nIndexIncrement == 0 ) || ( m_nIndexCount < m_nMaxIndexCount - 1 ) ); m_pMemory[m_nIndexCount] = nIndex1; m_pMemory[m_nIndexCount+1] = nIndex2; m_nIndexCount += m_nIndexIncrement + m_nIndexIncrement; } template< class T > inline void CIndexData::Index2( T nIndex1, T nIndex2 ) { COMPILE_TIME_ASSERT( 0 ); } #if 0 template< class T > inline void CIndexData::FastTriangle( int startVert ) { startVert += m_nIndexOffset; m_pIndices[m_nCurrentIndex+0] = startVert; m_pIndices[m_nCurrentIndex+1] = startVert + 1; m_pIndices[m_nCurrentIndex+2] = startVert + 2; AdvanceIndices(3); } template< class T > inline void CIndexData::FastQuad( int startVert ) { startVert += m_nIndexOffset; m_pIndices[m_nCurrentIndex+0] = startVert; m_pIndices[m_nCurrentIndex+1] = startVert + 1; m_pIndices[m_nCurrentIndex+2] = startVert + 2; m_pIndices[m_nCurrentIndex+3] = startVert; m_pIndices[m_nCurrentIndex+4] = startVert + 2; m_pIndices[m_nCurrentIndex+5] = startVert + 3; AdvanceIndices(6); } template< class T > inline void CIndexData::FastPolygon( int startVert, int triangleCount ) { unsigned short *pIndex = &m_pIndices[m_nCurrentIndex]; startVert += m_nIndexOffset; if ( !IsX360() ) { // NOTE: IndexSize is 1 or 0 (0 for alt-tab) // This prevents us from writing into bogus memory Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); triangleCount *= m_nIndexSize; } for ( int v = 0; v < triangleCount; ++v ) { *pIndex++ = startVert; *pIndex++ = startVert + v + 1; *pIndex++ = startVert + v + 2; } AdvanceIndices(triangleCount*3); } template< class T > inline void CIndexData::FastPolygonList( int startVert, int *pVertexCount, int polygonCount ) { unsigned short *pIndex = &m_pIndices[m_nCurrentIndex]; startVert += m_nIndexOffset; int indexOut = 0; if ( !IsX360() ) { // NOTE: IndexSize is 1 or 0 (0 for alt-tab) // This prevents us from writing into bogus memory Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); polygonCount *= m_nIndexSize; } for ( int i = 0; i < polygonCount; i++ ) { int vertexCount = pVertexCount[i]; int triangleCount = vertexCount-2; for ( int v = 0; v < triangleCount; ++v ) { *pIndex++ = startVert; *pIndex++ = startVert + v + 1; *pIndex++ = startVert + v + 2; } startVert += vertexCount; indexOut += triangleCount * 3; } AdvanceIndices(indexOut); } template< class T > inline void CIndexData::FastIndexList( const unsigned short *pIndexList, int startVert, int indexCount ) { unsigned short *pIndexOut = &m_pIndices[m_nCurrentIndex]; startVert += m_nIndexOffset; if ( !IsX360() ) { // NOTE: IndexSize is 1 or 0 (0 for alt-tab) // This prevents us from writing into bogus memory Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); indexCount *= m_nIndexSize; } for ( int i = 0; i < indexCount; ++i ) { pIndexOut[i] = startVert + pIndexList[i]; } AdvanceIndices(indexCount); } #endif //----------------------------------------------------------------------------- // Dynamic index field creation // NOTE: Draw call must occur prior to destruction of this class! //----------------------------------------------------------------------------- template< class T > class CDynamicIndexData : public CIndexData< T > { typedef CIndexData< T > BaseClass; public: CDynamicIndexData( IRenderContext* pRenderContext, int nIndexCount, const char *pDebugName, const char *pBudgetGroup ); // This constructor is meant to be used with instance rendering. // Passing in either 0 or INT_MAX here means the client code // doesn't know how many instances will be rendered. CDynamicIndexData( IRenderContext* pRenderContext, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ); ~CDynamicIndexData(); void Release(); // Begins, ends modification of the vertex buffer (returns true if the lock succeeded) // A lock may not succeed if there isn't enough room bool Lock( ); // Binds the vb to a particular slot using a particular offset void Bind( int nOffset ); private: void Init( IRenderContext* pRenderContext, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ); }; //----------------------------------------------------------------------------- // // Inline methods related to CDynamicIndexData // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- template< class T > inline CDynamicIndexData::CDynamicIndexData( IRenderContext* pRenderContext, int nIndexCount, const char *pDebugName, const char *pBudgetGroup ) : BaseClass( pRenderContext, RENDER_BUFFER_HANDLE_INVALID ) { Init( pRenderContext, nIndexCount, 0, pDebugName, pBudgetGroup ); } template< class T > inline CDynamicIndexData::CDynamicIndexData( IRenderContext* pRenderContext, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ) : BaseClass( pRenderContext, RENDER_BUFFER_HANDLE_INVALID ) { if ( nMaxInstanceCount == 0 ) { nMaxInstanceCount = INT_MAX; } Init( pRenderContext, nIndexCount, nMaxInstanceCount, pDebugName, pBudgetGroup ); } template< class T > inline void CDynamicIndexData::Init( IRenderContext* pRenderContext, int nIndexCount, int nMaxInstanceCount, const char *pDebugName, const char *pBudgetGroup ) { BufferDesc_t indexDesc; indexDesc.m_nElementSizeInBytes = sizeof(T); indexDesc.m_nElementCount = nIndexCount; indexDesc.m_pDebugName = pDebugName; indexDesc.m_pBudgetGroupName = pBudgetGroup; this->m_hIndexBuffer = pRenderContext->CreateDynamicIndexBuffer( indexDesc, nMaxInstanceCount ); this->m_nBufferIndexCount = nIndexCount; ResourceAddRef( m_hIndexBuffer ); } template< class T > inline CDynamicIndexData::~CDynamicIndexData() { Release(); } //----------------------------------------------------------------------------- // Release //----------------------------------------------------------------------------- template< class T > void CDynamicIndexData::Release() { if ( this->m_hIndexBuffer != RENDER_BUFFER_HANDLE_INVALID ) { this->m_pRenderContext->DestroyDynamicIndexBuffer( this->m_hIndexBuffer ); ResourceRelease( m_hIndexBuffer ); this->m_hIndexBuffer = RENDER_BUFFER_HANDLE_INVALID; } } //----------------------------------------------------------------------------- // Begins, ends modification of the buffer //----------------------------------------------------------------------------- template< class T > inline bool CDynamicIndexData::Lock( ) { Assert( this->m_hIndexBuffer != RENDER_BUFFER_HANDLE_INVALID ); return BaseClass::Lock( ); } //----------------------------------------------------------------------------- // Binds the ib to a particular stream using a particular offset //----------------------------------------------------------------------------- template< class T > inline void CDynamicIndexData::Bind( int nOffset ) { this->m_pRenderContext->BindIndexBuffer( this->m_hIndexBuffer, nOffset ); } #endif // INDEXDATA_H