summaryrefslogtreecommitdiff
path: root/public/panorama/uipbmsgbase.h
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/panorama/uipbmsgbase.h
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'public/panorama/uipbmsgbase.h')
-rw-r--r--public/panorama/uipbmsgbase.h597
1 files changed, 597 insertions, 0 deletions
diff --git a/public/panorama/uipbmsgbase.h b/public/panorama/uipbmsgbase.h
new file mode 100644
index 0000000..141eb9c
--- /dev/null
+++ b/public/panorama/uipbmsgbase.h
@@ -0,0 +1,597 @@
+//=========== Copyright Valve Corporation, All rights reserved. ===============//
+//
+// Purpose:
+//=============================================================================//
+
+#ifndef PROTOBUFPOOL_H
+#define PROTOBUFPOOL_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "tier0/tslist.h"
+#include "tier1/fmtstr.h"
+#include "tier1/utlstring.h"
+#include "tier1/utlvector.h"
+#if !defined( SOURCE2_PANORAMA )
+#include "tier1/tsmempool.h"
+#include "misc.h"
+#endif
+#include "tier0/vprof.h"
+
+#include "protobuf-2.3.0/src/google/protobuf/message_lite.h"
+
+namespace panorama
+{
+
+#define PBMEM_POOL_LOW_TARGET 256
+#define PBMEM_POOL_HIGH_TARGET 512
+
+class IProtoBufMsgMemoryPool
+{
+public:
+
+ virtual ~IProtoBufMsgMemoryPool() {}
+
+ // Methods that need to be exposed out to examine memory
+ virtual uint32 GetEstimatedSize() = 0;
+ virtual uint32 GetCount() = 0;
+ virtual CUtlString GetName() = 0;
+ virtual uint32 GetAllocHitCount() = 0;
+ virtual uint32 GetAllocMissCount() = 0;
+ virtual void Free( void *pObjectVoid ) = 0;
+
+#ifdef DBGFLAG_VALIDATE
+ virtual void Validate( CValidator &validator, const char *pchName ) = 0;
+#endif
+};
+
+
+#if defined( SOURCE2_PANORAMA )
+#include <tier0/memdbgon.h>
+#endif
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsgMemoryPool - Implementation for allocation pools for protobufmsgs.
+// We create one of these per protobuf msg type, created on first construction of
+// an object of that type.
+//-----------------------------------------------------------------------------
+template< typename PB_OBJECT_TYPE >
+class CUIProtoBufMsgMemoryPool : public IProtoBufMsgMemoryPool
+{
+public:
+
+ typedef typename CTSList< PB_OBJECT_TYPE * >::Node_t Node;
+
+ CUIProtoBufMsgMemoryPool( uint32 unTargetLow, uint32 unTargetHigh )
+ {
+ m_unTargetCountLow = unTargetLow;
+ m_unTargetCountHigh = unTargetHigh;
+ m_unEstimatedSize = 0;
+ m_unAllocHitCounter = 0;
+ m_unAllocMissCounter = 0;
+ m_pTListFreeObjects = new CTSList< PB_OBJECT_TYPE * >();
+ }
+
+
+ ~CUIProtoBufMsgMemoryPool()
+ {
+ Node *pObject = m_pTListFreeObjects->Pop();
+ while ( pObject )
+ {
+ Destruct( pObject->elem );
+#if defined( SOURCE2_PANORAMA )
+ free( pObject->elem );
+#else
+ FreePv( pObject->elem );
+#endif
+ delete pObject;
+
+ pObject = m_pTListFreeObjects->Pop();
+ }
+ m_pTListFreeObjects->Purge();
+ delete m_pTListFreeObjects;
+ }
+
+ CUtlString GetName()
+ {
+ return PB_OBJECT_TYPE::default_instance().GetTypeName().c_str();
+ }
+
+ uint32 GetEstimatedSize()
+ {
+ return m_pTListFreeObjects->Count()*m_unEstimatedSize;
+ }
+
+ uint32 GetCount()
+ {
+ return m_pTListFreeObjects->Count();
+ }
+
+ uint32 GetAllocHitCount()
+ {
+ return m_unAllocHitCounter;
+ }
+
+ uint32 GetAllocMissCount()
+ {
+ return m_unAllocMissCounter;
+ }
+
+
+ Node * AllocProtoBuf()
+ {
+ Node *pObject = m_pTListFreeObjects->Pop();
+ if ( !pObject )
+ {
+ ++m_unAllocMissCounter;
+ pObject = new Node;
+#if defined( SOURCE2_PANORAMA )
+ pObject->elem = (PB_OBJECT_TYPE *)malloc( sizeof( PB_OBJECT_TYPE ) );
+#else
+ pObject->elem = (PB_OBJECT_TYPE *)PvAllocNoLeakTracking( sizeof( PB_OBJECT_TYPE ) );
+#endif
+ Construct( pObject->elem );
+ }
+ else
+ {
+ ++m_unAllocHitCounter;
+ bool bFreeAnother = false;
+ uint32 unCount = m_pTListFreeObjects->Count();
+
+ // We'll free an extra cached msg every alloc if we are over the higher limit, and every 6th if we
+ // are over the lower limit. This allows some elasticity to peaks in demand.
+ if ( unCount > m_unTargetCountHigh )
+ bFreeAnother = true;
+ else if ( unCount > m_unTargetCountLow && m_unAllocHitCounter % 6 == 0 )
+ bFreeAnother = true;
+
+ if ( bFreeAnother )
+ {
+ // Pop an extra item, so we can get down to target count over time
+ Node *pThrowAway = m_pTListFreeObjects->Pop();
+ if ( pThrowAway )
+ {
+ Destruct( pThrowAway->elem );
+#if defined( SOURCE2_PANORAMA )
+ free( pThrowAway->elem );
+#else
+ FreePv( pThrowAway->elem );
+#endif
+ delete pThrowAway;
+ }
+ }
+ }
+ return pObject;
+ }
+
+ inline void Free( void *pObjectVoid )
+ {
+ Free( (Node *)pObjectVoid );
+ }
+
+ void Free( Node *pObject )
+ {
+ if ( m_unFreeCounter++ % 2000 == 0 || m_unEstimatedSize == 0 )
+ {
+ m_unEstimatedSize = pObject->elem->SpaceUsed();
+ }
+
+ if ( m_unTargetCountHigh > 0 )
+ {
+ // We always cache for re-use on free, though we may throw out later on alloc to shrink the pool
+ pObject->elem->Clear();
+ m_pTListFreeObjects->Push( pObject );
+ }
+ else
+ {
+ Destruct( pObject->elem );
+#if defined( SOURCE2_PANORAMA )
+ free( pObject->elem );
+#else
+ FreePv( pObject->elem );
+#endif
+ delete pObject;
+ }
+ }
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ m_pTListFreeObjects->Validate( validator, "m_pTListFreeObjects" );
+ validator.ClaimMemory_Aligned( m_pTListFreeObjects );
+ }
+#endif // DBGFLAG_VALIDATE
+
+private:
+ CTSList< PB_OBJECT_TYPE * > *m_pTListFreeObjects;
+
+ // Not critical for these to be "right" so they don't need to be thread safe
+ uint32 m_unEstimatedSize;
+ uint32 m_unFreeCounter;
+
+ // These counters are important to get correct, so interlocked in case of allocating on threads
+ CInterlockedInt m_unAllocHitCounter;
+ CInterlockedInt m_unAllocMissCounter;
+
+ // Only set at construction, so not needed to be thread safe
+ uint32 m_unTargetCountLow;
+ uint32 m_unTargetCountHigh;
+};
+
+#if defined( SOURCE2_PANORAMA )
+#include <tier0/memdbgoff.h>
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Ref count wrapper around protobuf message objects
+//-----------------------------------------------------------------------------
+class CMsgLiteRefCount
+{
+public:
+ CMsgLiteRefCount()
+ {
+ m_pMemoryPool = NULL;
+ m_pTSListNode = NULL;
+ m_cRef = 1;
+ }
+
+ // Has to be public, so we can use in memory pool, should always already be at zero ref count though
+ ~CMsgLiteRefCount() { DbgAssert( 0 == m_cRef ); }
+
+ inline int AddRef()
+ {
+ return ThreadInterlockedIncrement( &m_cRef );
+ }
+
+ int Release();
+
+ inline google::protobuf::MessageLite *AccessMsg()
+ {
+ return (google::protobuf::MessageLite *)(((CTSList<void *>::Node_t *)m_pTSListNode)->elem);
+ }
+
+ IProtoBufMsgMemoryPool *m_pMemoryPool;
+ void *m_pTSListNode;
+ void *m_pSelfNode;
+
+#ifdef DBGFLAG_VALIDATE
+ virtual void Validate( CValidator &validator, const tchar *pchName )
+ {
+ VALIDATE_SCOPE();
+
+
+ if ( m_cRef != 0 )
+ {
+ CTSList<void *>::Node_t *pNode = (CTSList<void *>::Node_t *)m_pSelfNode;
+
+ if ( pNode )
+ {
+
+ void *pvMem = MemAlloc_Unalign( pNode );
+ if ( !validator.IsClaimed( pvMem ) )
+ validator.ClaimMemory_Aligned( pNode );
+
+ if ( !validator.IsClaimed( pNode->elem ) )
+ validator.ClaimMemory( pNode->elem );
+ }
+
+ pNode = (CTSList<void *>::Node_t *)m_pTSListNode;
+ if ( pNode )
+ {
+ void *pvMem = MemAlloc_Unalign( pNode );
+ if ( !validator.IsClaimed( pvMem ) )
+ validator.ClaimMemory_Aligned( pNode );
+ }
+ }
+
+ }
+#endif
+
+private:
+
+ friend class CUIProtoBufMsgMemoryPoolMgr;
+
+ volatile int32 m_cRef;
+};
+
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsgMemoryPoolMgr - Manages all the message pools for render messages proto buf objects.
+// Should have one global singleton instance of this which tracks all the pools
+// for individual message types.
+//-----------------------------------------------------------------------------
+class CUIProtoBufMsgMemoryPoolMgr
+{
+public:
+
+ CUIProtoBufMsgMemoryPoolMgr()
+ {
+ m_pTSListMsgLiteRefCount = new CTSList<CMsgLiteRefCount*>;
+ }
+
+ ~CUIProtoBufMsgMemoryPoolMgr()
+ {
+ CTSList<CMsgLiteRefCount*>::Node_t *pNode = m_pTSListMsgLiteRefCount->Pop();
+ while ( pNode )
+ {
+ delete pNode->elem;
+ delete pNode;
+ pNode = m_pTSListMsgLiteRefCount->Pop();
+ }
+
+ FOR_EACH_VEC( m_vecMsgPools, i )
+ {
+ delete m_vecMsgPools[i];
+ }
+ m_vecMsgPools.RemoveAll();
+ }
+
+ CTSList<CMsgLiteRefCount*>::Node_t * AllocMsgLiteRef()
+ {
+ CTSList<CMsgLiteRefCount*>::Node_t *pNode = m_pTSListMsgLiteRefCount->Pop();
+ if ( !pNode )
+ {
+ pNode = new CTSList<CMsgLiteRefCount*>::Node_t;
+ pNode->elem = new CMsgLiteRefCount();
+ return pNode;
+ }
+ else
+ {
+ DbgAssert( pNode->elem->m_cRef == 0 );
+ pNode->elem->m_cRef = 1;
+ }
+ return pNode;
+ }
+
+ void FreeMsgLiteRef( CTSList<CMsgLiteRefCount*>::Node_t * pRef )
+ {
+#if defined(_DEBUG) && !defined(NO_MALLOC_OVERRIDE) && !defined( SOURCE2_PANORAMA )
+ Assert( g_pMemAllocSteam->IsValid( pRef->elem ) );
+#endif
+ m_pTSListMsgLiteRefCount->Push( pRef );
+ }
+
+ void RegisterPool( IProtoBufMsgMemoryPool * pPool )
+ {
+ m_vecMsgPools.AddToTail( pPool );
+ }
+
+ void DumpPoolInfo()
+ {
+ uint32 unTotalSize = 0;
+ Msg( "CRenderMsgMemoryPoolMgr:\n" );
+ Msg( " PoolName Count Est. Size Hit Rate\n" );
+ Msg( " ----------------------------------------- ----------- ----------- -----------\n" );
+ FOR_EACH_VEC( m_vecMsgPools, i )
+ {
+ uint32 unHitCount = m_vecMsgPools[i]->GetAllocHitCount();
+ uint32 unMissCount = m_vecMsgPools[i]->GetAllocMissCount();
+ float flHitRate = 0.0f;
+ if ( unHitCount > 0 || unMissCount > 0 )
+ {
+ flHitRate = (float)unHitCount / (float)(unHitCount+unMissCount);
+ flHitRate *= 100.0f;
+ }
+
+ uint32 unEstimate = m_vecMsgPools[i]->GetEstimatedSize();
+ Msg( "%43s%12d%12s%12s\n", m_vecMsgPools[i]->GetName().String(), m_vecMsgPools[i]->GetCount(), V_pretifymem( (float)unEstimate, 2, true ), CFmtStr( "%f%%", flHitRate ).Access() );
+ unTotalSize += unEstimate;
+ }
+ Msg( " -----------------------------------------------------------------------------\n" );
+ Msg( " Total: %s\n", V_pretifymem( (float)unTotalSize, 2, true ) );
+
+ Msg( " -----------------------------------------------------------------------------\n" );
+ Msg( "TSList for CMsgLiteRef size: %s\n", V_pretifymem( (float)m_pTSListMsgLiteRefCount->Count(), 2, true ) );
+ }
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ ValidateObj( m_vecMsgPools );
+ FOR_EACH_VEC( m_vecMsgPools, i )
+ {
+ ValidatePtr( m_vecMsgPools[i] );
+ }
+ validator.ClaimMemory_Aligned( m_pTSListMsgLiteRefCount );
+ m_pTSListMsgLiteRefCount->Validate( validator, "m_pTSListMsgLiteRefCount" );
+
+ CUtlVector< CTSList<CMsgLiteRefCount *>::Node_t * > vecTemp;
+
+ CTSList<CMsgLiteRefCount *>::Node_t *pNode = m_pTSListMsgLiteRefCount->Pop();
+ while ( pNode )
+ {
+ ValidatePtrIfNeeded( pNode->elem );
+ vecTemp.AddToTail( pNode );
+ pNode = m_pTSListMsgLiteRefCount->Pop();
+ }
+
+ FOR_EACH_VEC( vecTemp, i )
+ {
+ m_pTSListMsgLiteRefCount->Push( vecTemp[i] );
+ }
+ }
+#endif // DBGFLAG_VALIDATE
+
+private:
+ CUtlVector< IProtoBufMsgMemoryPool * > m_vecMsgPools;
+ CTSList<CMsgLiteRefCount *> *m_pTSListMsgLiteRefCount;
+};
+
+
+// Interface that rendermsgs of all types implement
+class IUIProtoBufMsg
+{
+public:
+ virtual ~IUIProtoBufMsg() {}
+ virtual void SerializeInProc( CUtlBuffer *pBuffer ) const = 0;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Base class for protobuf objects
+//-----------------------------------------------------------------------------
+template< typename PB_OBJECT_TYPE >
+class CUIProtoBufMsg : public IUIProtoBufMsg
+{
+private:
+ static bool s_bRegisteredWithMemoryPoolMgr;
+ static CThreadMutex s_Mutex;
+ static CUIProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *s_pMemoryPool;
+
+public:
+
+ typedef typename CTSList<PB_OBJECT_TYPE *>::Node_t Node;
+
+ // Called on construction of each object of this type, but only does work
+ // once to setup memory pools for the class type.
+ static void OneTimeInit()
+ {
+ // If we haven't done registration do so now
+ if ( !s_bRegisteredWithMemoryPoolMgr )
+ {
+ // Get the lock and make sure we still haven't
+ s_Mutex.Lock();
+ if ( !s_bRegisteredWithMemoryPoolMgr )
+ {
+ s_pMemoryPool = new CUIProtoBufMsgMemoryPool< PB_OBJECT_TYPE >( PBMEM_POOL_LOW_TARGET, PBMEM_POOL_HIGH_TARGET );
+ UIEngine()->MsgMemoryPoolMgr()->RegisterPool( s_pMemoryPool );
+ s_bRegisteredWithMemoryPoolMgr = true;
+ }
+ s_Mutex.Unlock();
+ }
+ }
+
+ CUIProtoBufMsg()
+ {
+ OneTimeInit();
+ CTSList<CMsgLiteRefCount *>::Node_t *pRefCountNode = UIEngine()->MsgMemoryPoolMgr()->AllocMsgLiteRef();
+ m_pMsgRefCount = pRefCountNode->elem;
+ m_pMsgRefCount->m_pSelfNode = pRefCountNode;
+ m_pMsgRefCount->m_pMemoryPool = s_pMemoryPool;
+ Node *pNode = s_pMemoryPool->AllocProtoBuf();
+ m_pMsgRefCount->m_pTSListNode = pNode;
+ m_bIsValid = true;
+ }
+
+ // Expose memory pool for direct allocation of underlying PB msg objects
+ static Node *AllocProtoBufMsgObject()
+ {
+ OneTimeInit();
+ return s_pMemoryPool->AllocProtoBuf();
+ }
+
+ // Expose memory pool for direct allocation of underlying PB msg objects
+ static void FreeProtoBufMsgObject( Node *pMsg )
+ {
+ s_pMemoryPool->Free( pMsg );
+ }
+
+ // Construct and deserialize in one
+ CUIProtoBufMsg( CUtlBuffer *pBuffer )
+ {
+ OneTimeInit();
+ m_pMsgRefCount = NULL;
+ m_bIsValid = BDeserializeInProc( pBuffer );
+ }
+
+ // Destructor
+ virtual ~CUIProtoBufMsg()
+ {
+ CleanupAllocations();
+ }
+
+ bool BIsValid() { return m_bIsValid; }
+
+ inline void SerializeInProc( CUtlBuffer *pBuffer ) const
+ {
+
+ // Ensure enough for type, size, and serialized data
+ pBuffer->EnsureCapacity( pBuffer->TellPut() + sizeof(uint32) + sizeof( uint64 ) );
+
+ m_pMsgRefCount->AddRef();
+ pBuffer->PutUnsignedInt( (int)m_eCmd );
+ pBuffer->PutUnsignedInt64( (uint64)m_pMsgRefCount );
+ }
+
+ inline bool BDeserializeInProc( CUtlBuffer *pBuffer )
+ {
+ if ( pBuffer->GetBytesRemaining() < (int)sizeof(uint64) )
+ return false;
+
+ uint64 ulPtr = pBuffer->GetUnsignedInt64();
+ if ( ulPtr == 0 )
+ return false;
+
+ CleanupAllocations();
+ m_pMsgRefCount = (CMsgLiteRefCount*)ulPtr;
+ m_pMsgRefCount->AddRef();
+ return true;
+ }
+
+ void SerializeCrossProc( CUtlBuffer *pBuffer ) const
+ {
+ VPROF_BUDGET( "CUIProtoBufMsg::SerializeCrossProc", VPROF_BUDGETGROUP_TENFOOT );
+ uint32 unSize = m_pMsgRefCount->AccessMsg()->ByteSize();
+
+ // Ensure enough for type, size, and serialized data
+ pBuffer->EnsureCapacity( pBuffer->TellPut() + sizeof(uint32) * 3 + unSize ); // bugbug cboyd - drop to * 2 whenpassthrough is removed below
+
+ pBuffer->PutUnsignedInt( (int)m_eCmd );
+ pBuffer->PutUnsignedInt( unSize );
+
+ if ( unSize == 0 )
+ return;
+
+ uint8 *pBody = (uint8*)pBuffer->Base()+pBuffer->TellPut();
+ m_pMsgRefCount->AccessMsg()->SerializeWithCachedSizesToArray( pBody );
+ pBuffer->SeekPut( CUtlBuffer::SEEK_CURRENT, unSize );
+ }
+
+ bool BDeserializeCrossProc( CUtlBuffer *pBuffer )
+ {
+ VPROF_BUDGET( "CUIProtoBufMsg::BDeserialize", VPROF_BUDGETGROUP_TENFOOT );
+ if ( pBuffer->GetBytesRemaining() < (int)sizeof(uint32) )
+ return false;
+ uint32 unSize = pBuffer->GetUnsignedInt();
+
+ if ( unSize == 0 )
+ return true;
+
+ if ( pBuffer->GetBytesRemaining() < (int)unSize )
+ return false;
+
+ bool bSucccess = m_pMsgRefCount->AccessMsg()->ParseFromArray( (uint8*)pBuffer->Base()+pBuffer->TellGet(), unSize );
+ pBuffer->SeekGet( CUtlBuffer::SEEK_CURRENT, unSize );
+
+ return bSucccess;
+ }
+
+ // Accessors
+ PB_OBJECT_TYPE &Body() { return *((PB_OBJECT_TYPE*)(m_pMsgRefCount->AccessMsg())); }
+ const PB_OBJECT_TYPE &BodyConst() const { return *((const PB_OBJECT_TYPE*)(m_pMsgRefCount->AccessMsg())); }
+
+protected:
+ CMsgLiteRefCount *m_pMsgRefCount;
+ int m_eCmd;
+ bool m_bIsValid;
+private:
+
+ void CleanupAllocations()
+ {
+ SAFE_RELEASE( m_pMsgRefCount );
+ }
+
+};
+
+
+// Statics
+template< typename PB_OBJECT_TYPE > bool CUIProtoBufMsg< PB_OBJECT_TYPE>::s_bRegisteredWithMemoryPoolMgr = false;
+template< typename PB_OBJECT_TYPE > CThreadMutex CUIProtoBufMsg< PB_OBJECT_TYPE>::s_Mutex;
+template< typename PB_OBJECT_TYPE > CUIProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *CUIProtoBufMsg< PB_OBJECT_TYPE>::s_pMemoryPool = NULL;
+
+} // namespace panorama
+
+#endif //PROTOBUFPOOL_H