//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: CGPUBufferAllocator manages allocation of VBs/IBs from shared memory pools. // Avoids 4KB physical alloc alignment overhead per VB/IB. // // $NoKeywords: $ // //===========================================================================// #ifndef GPUBUFFERALLOCATOR_H #define GPUBUFFERALLOCATOR_H #ifdef _WIN32 #pragma once #endif #ifdef _X360 #include "tier1/utlvector.h" #include "tier1/convar.h" class CVertexBuffer; class CIndexBuffer; // Only active on X360 atm #define USE_GPU_BUFFER_ALLOCATOR ( IsX360() ) //----------------------------------------------------------------------------- // A handle to a buffer pool allocation, held by the allocated VB/IB //----------------------------------------------------------------------------- struct GPUBufferHandle_t { GPUBufferHandle_t( void ) : nPoolNum( -1 ), pMemory( NULL ), nPoolEntry( -1 ) {} bool IsValid( void ) { return ( pMemory != NULL ); } byte * pMemory; // Physical address of the allocation int nPoolNum; // Identifies the pool int nPoolEntry; // Identifies this allocation within the pool }; //----------------------------------------------------------------------------- // Describes an entry in a CGPUBufferPool //----------------------------------------------------------------------------- struct GPUBufferPoolEntry_t { int nOffset; int nSize; bool bIsVertexBuffer; union { // These are set to NULL by CGPUBufferPool::Free() (called when the VB/IB is destroyed) CVertexBuffer *pVertexBuffer; CIndexBuffer *pIndexBuffer; }; }; //----------------------------------------------------------------------------- // A single memory block out of which individual VBs/IBs are allocated //----------------------------------------------------------------------------- class CGPUBufferPool { public: CGPUBufferPool( int nSize ); virtual ~CGPUBufferPool( void ); // Returns the index (-1 on failure) of a new allocation in the pool, for a buffer of the given size. int Allocate( int nSize, bool bIsVertexBuffer, void *pObject ); // Frees a given entry (just marks it as freed, the memory will not be reused by Allocate() until CGPUBufferAllocator::Defrag() is called ) void Deallocate( const GPUBufferHandle_t *pHandle ); private: // NOTE: these values are specialized for X360 and should be #ifdef'd for other target platforms static const int POOL_ENTRIES_INIT_SIZE = 256; static const int POOL_ENTRIES_GROW_SIZE = 256; static const int POOL_ENTRY_ALIGNMENT = 4; // 4-byte alignment required for VB/IB data on XBox360 byte * m_pMemory; // Pointer to the (physical) address of the pool's memory int m_nSize; // Total size of the pool int m_nBytesUsed; // High watermark of used memory in the pool CUtlVectorm_PoolEntries; // Memory-order array of items allocated in the pool // CGPUBufferAllocator is a friend so that CGPUBufferAllocator::Defrag() can shuffle allocations around friend class CGPUBufferAllocator; }; //----------------------------------------------------------------------------- // Manages a set of memory blocks out of which individual VBs/IBs are allocated //----------------------------------------------------------------------------- class CGPUBufferAllocator { public: CGPUBufferAllocator( void ); virtual ~CGPUBufferAllocator( void ); // (De)Allocates memory for a vertex/index buffer: bool AllocateVertexBuffer( CVertexBuffer *pVertexBuffer, int nBufferSize ); bool AllocateIndexBuffer( CIndexBuffer *pIndexBuffer, int nBufferSize ); void DeallocateVertexBuffer( CVertexBuffer *pVertexBuffer ); void DeallocateIndexBuffer( CIndexBuffer *pIndexBuffer ); // Compact memory to account for freed buffers // NOTE: this must only be called during map transitions, no rendering must be in flight and everything must be single-threaded! void Compact(); // Spew statistics about pooled buffer allocations void SpewStats( bool bBrief = false ); private: // NOTE: these values are specialized for X360 and should be #ifdef'd for other target platforms static const int INITIAL_POOL_SIZE = 57*1024*1024 + 256*1024; static const int ADDITIONAL_POOL_SIZE = 2*1024*1024; static const int MAX_POOLS = 8; static const int MAX_BUFFER_SIZE = ADDITIONAL_POOL_SIZE; // 256*1024; // Allocate a new CGPUBufferPool bool AllocatePool( int nPoolSize ); // Allocate/deallocate a buffer (type-agnostic) bool AllocateBuffer( GPUBufferHandle_t *pHandle, int nBufferSize, void *pObject, bool bIsVertexBuffer ); void DeallocateBuffer( const GPUBufferHandle_t *pHandle ); // Make a handle for a given allocation GPUBufferHandle_t MakeGPUBufferHandle( int nPoolNum, int nPoolEntry ); // Helper for Compact void MoveBufferMemory( int nDstPool, int *pnDstEntry, int *pnDstOffset, CGPUBufferPool &srcPool, GPUBufferPoolEntry_t &srcEntry ); CGPUBufferPool *m_BufferPools[ MAX_POOLS ]; int m_nBufferPools; bool m_bEnabled; CThreadFastMutex m_mutex; }; // Track non-pooled physallocs, to help tune CGPUBufferAllocator usage: extern CInterlockedInt g_NumIndividualIBPhysAllocs; extern CInterlockedInt g_SizeIndividualIBPhysAllocs; extern CInterlockedInt g_NumIndividualVBPhysAllocs; extern CInterlockedInt g_SizeIndividualVBPhysAllocs; #endif // _X360 #endif // GPUBUFFERALLOCATOR_H