//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Real-Time Hierarchical Profiling
//
// $NoKeywords: $
//===========================================================================//
# include "pch_tier0.h"
# include "tier0/memalloc.h"
# include "tier0/valve_off.h"
# if defined(_WIN32) && !defined(_X360)
# define WIN_32_LEAN_AND_MEAN
# include <windows.h>
# endif
# include <assert.h>
# ifdef _WIN32
# pragma warning(disable:4073)
# pragma init_seg( lib )
# endif
# pragma warning(push, 1)
# pragma warning(disable:4786)
# pragma warning(disable:4530)
# include <map>
# include <vector>
# include <algorithm>
# pragma warning(pop)
# include "tier0/valve_on.h"
# include "tier0/vprof.h"
# include "tier0/l2cache.h"
# include "tier0/tslist.h"
# include "tier0/dynfunction.h"
# ifdef _X360
# include "xbox/xbox_console.h"
# else // NOT _X360:
# include "tier0/memdbgon.h"
# endif
// NOTE: Explicitly and intentionally using STL in here to not generate any
// cyclical dependencies between the low-level debug library and the higher
// level data structures (toml 01-27-03)
using namespace std ;
# ifdef VPROF_ENABLED
# if defined(_X360) && !defined(_CERT) // enable PIX CPU trace:
# include "tracerecording.h"
# pragma comment( lib, "tracerecording.lib" )
# pragma comment( lib, "xbdm.lib" )
# endif
//-----------------------------------------------------------------------------
bool g_VProfSignalSpike ;
//-----------------------------------------------------------------------------
CVProfile g_VProfCurrentProfile ;
int CVProfNode : : s_iCurrentUniqueNodeID = 0 ;
CVProfNode : : ~ CVProfNode ( )
{
# if !defined( _WIN32 ) && !defined( POSIX )
delete m_pChild ;
delete m_pSibling ;
# endif
}
CVProfNode * CVProfNode : : GetSubNode ( const tchar * pszName , int detailLevel , const tchar * pBudgetGroupName , int budgetFlags )
{
// Try to find this sub node
CVProfNode * child = m_pChild ;
while ( child )
{
if ( child - > m_pszName = = pszName )
{
return child ;
}
child = child - > m_pSibling ;
}
// We didn't find it, so add it
CVProfNode * node = new CVProfNode ( pszName , detailLevel , this , pBudgetGroupName , budgetFlags ) ;
node - > m_pSibling = m_pChild ;
m_pChild = node ;
return node ;
}
CVProfNode * CVProfNode : : GetSubNode ( const tchar * pszName , int detailLevel , const tchar * pBudgetGroupName )
{
return GetSubNode ( pszName , detailLevel , pBudgetGroupName , BUDGETFLAG_OTHER ) ;
}
//-------------------------------------
void CVProfNode : : EnterScope ( )
{
m_nCurFrameCalls + + ;
if ( m_nRecursions + + = = 0 )
{
m_Timer . Start ( ) ;
# ifndef _X360
if ( g_VProfCurrentProfile . UsePME ( ) )
{
m_L2Cache . Start ( ) ;
}
# else // 360 code:
if ( g_VProfCurrentProfile . UsePME ( ) | | ( ( m_iBitFlags & kRecordL2 ) ! = 0 ) )
{
m_PMCData . Start ( ) ;
}
if ( ( m_iBitFlags & kCPUTrace ) ! = 0 )
{
// this node is to be recorded. Which recording mode are we in?
switch ( g_VProfCurrentProfile . GetCPUTraceMode ( ) )
{
case CVProfile : : kFirstHitNode :
case CVProfile : : kAllNodesInFrame_Recording :
case CVProfile : : kAllNodesInFrame_RecordingMultiFrame :
// we are presently recording.
if ( ! XTraceStartRecording ( g_VProfCurrentProfile . GetCPUTraceFilename ( ) ) )
{
Msg ( " XTraceStartRecording failed, error code %d \n " , GetLastError ( ) ) ;
}
default :
// no default.
break ;
}
}
# endif
# ifdef VPROF_VTUNE_GROUP
g_VProfCurrentProfile . PushGroup ( m_BudgetGroupID ) ;
# endif
}
}
//-------------------------------------
bool CVProfNode : : ExitScope ( )
{
if ( - - m_nRecursions = = 0 & & m_nCurFrameCalls ! = 0 )
{
m_Timer . End ( ) ;
m_CurFrameTime + = m_Timer . GetDuration ( ) ;
# ifndef _X360
if ( g_VProfCurrentProfile . UsePME ( ) )
{
m_L2Cache . End ( ) ;
m_iCurL2CacheMiss + = m_L2Cache . GetL2CacheMisses ( ) ;
}
# else // 360 code:
if ( g_VProfCurrentProfile . UsePME ( ) | | ( ( m_iBitFlags & kRecordL2 ) ! = 0 ) )
{
m_PMCData . End ( ) ;
m_iCurL2CacheMiss + = m_PMCData . GetL2CacheMisses ( ) ;
m_iCurLoadHitStores + = m_PMCData . GetLHS ( ) ;
}
if ( ( m_iBitFlags & kCPUTrace ) ! = 0 )
{
// this node is enabled to be recorded. What mode are we in?
switch ( g_VProfCurrentProfile . GetCPUTraceMode ( ) )
{
case CVProfile : : kFirstHitNode :
{
// one-off recording. stop now.
if ( XTraceStopRecording ( ) )
{
Msg ( " CPU trace finished. \n " ) ;
if ( g_VProfCurrentProfile . TraceCompleteEvent ( ) )
{
// signal VXConsole that trace is completed
XBX_rTraceComplete ( ) ;
}
}
// don't trace again next frame, overwriting the file.
g_VProfCurrentProfile . SetCPUTraceEnabled ( CVProfile : : kDisabled ) ;
break ;
}
case CVProfile : : kAllNodesInFrame_Recording :
case CVProfile : : kAllNodesInFrame_RecordingMultiFrame :
{
// one-off recording. stop now.
if ( XTraceStopRecording ( ) )
{
if ( g_VProfCurrentProfile . GetCPUTraceMode ( ) = = CVProfile : : kAllNodesInFrame_RecordingMultiFrame )
{
Msg ( " %.3f msec in %s \n " , m_CurFrameTime . GetMillisecondsF ( ) , g_VProfCurrentProfile . GetCPUTraceFilename ( ) ) ;
}
else
{
Msg ( " CPU trace finished. \n " ) ;
}
}
// Spew time info for file to allow figuring it out later
g_VProfCurrentProfile . LatchMultiFrame ( m_CurFrameTime . GetLongCycles ( ) ) ;
#if 0 // This doesn't want to work on the xbox360-- MoveFile not available or file still being put down to disk?
char suffix [ 32 ] ;
_snprintf ( suffix , sizeof ( suffix ) , " _%.3f_msecs " , flMsecs ) ;
char fn [ 512 ] ;
strncpy ( fn , g_VProfCurrentProfile . GetCPUTraceFilename ( ) , sizeof ( fn ) ) ;
char * p = strrchr ( fn , ' . ' ) ;
if ( * p )
{
* p = 0 ;
}
strncat ( fn , suffix , sizeof ( fn ) ) ;
strncat ( fn , " .pix2 " , sizeof ( fn ) ) ;
BOOL bSuccess = MoveFile ( g_VProfCurrentProfile . GetCPUTraceFilename ( ) , fn ) ;
if ( ! bSuccess )
{
DWORD eCode = GetLastError ( ) ;
Msg ( " Error %d \n " , eCode ) ;
}
# endif
// we're still recording until the frame is done.
// but, increment the index.
g_VProfCurrentProfile . IncrementMultiTraceIndex ( ) ;
break ;
}
}
// g_VProfCurrentProfile.IsCPUTraceEnabled() &&
}
# endif
# ifdef VPROF_VTUNE_GROUP
g_VProfCurrentProfile . PopGroup ( ) ;
# endif
}
return ( m_nRecursions = = 0 ) ;
}
//-------------------------------------
void CVProfNode : : Pause ( )
{
if ( m_nRecursions > 0 )
{
m_Timer . End ( ) ;
m_CurFrameTime + = m_Timer . GetDuration ( ) ;
# ifndef _X360
if ( g_VProfCurrentProfile . UsePME ( ) )
{
m_L2Cache . End ( ) ;
m_iCurL2CacheMiss + = m_L2Cache . GetL2CacheMisses ( ) ;
}
# else // 360 code:
if ( g_VProfCurrentProfile . UsePME ( ) | | ( ( m_iBitFlags & kRecordL2 ) ! = 0 ) )
{
m_PMCData . End ( ) ;
m_iCurL2CacheMiss + = m_PMCData . GetL2CacheMisses ( ) ;
m_iCurLoadHitStores + = m_PMCData . GetLHS ( ) ;
}
# endif
}
if ( m_pChild )
{
m_pChild - > Pause ( ) ;
}
if ( m_pSibling )
{
m_pSibling - > Pause ( ) ;
}
}
//-------------------------------------
void CVProfNode : : Resume ( )
{
if ( m_nRecursions > 0 )
{
m_Timer . Start ( ) ;
# ifndef _X360
if ( g_VProfCurrentProfile . UsePME ( ) )
{
m_L2Cache . Start ( ) ;
}
# else
if ( g_VProfCurrentProfile . UsePME ( ) | | ( ( m_iBitFlags & kRecordL2 ) ! = 0 ) )
{
m_PMCData . Start ( ) ;
}
# endif
}
if ( m_pChild )
{
m_pChild - > Resume ( ) ;
}
if ( m_pSibling )
{
m_pSibling - > Resume ( ) ;
}
}
//-------------------------------------
void CVProfNode : : Reset ( )
{
m_nPrevFrameCalls = 0 ;
m_PrevFrameTime . Init ( ) ;
m_nCurFrameCalls = 0 ;
m_CurFrameTime . Init ( ) ;
m_nTotalCalls = 0 ;
m_TotalTime . Init ( ) ;
m_PeakTime . Init ( ) ;
m_iPrevL2CacheMiss = 0 ;
m_iCurL2CacheMiss = 0 ;
m_iTotalL2CacheMiss = 0 ;
# ifdef _X360
m_iPrevLoadHitStores = 0 ;
m_iCurLoadHitStores = 0 ;
m_iTotalLoadHitStores = 0 ;
# endif
if ( m_pChild )
{
m_pChild - > Reset ( ) ;
}
if ( m_pSibling )
{
m_pSibling - > Reset ( ) ;
}
}
//-------------------------------------
void CVProfNode : : MarkFrame ( )
{
m_nPrevFrameCalls = m_nCurFrameCalls ;
m_PrevFrameTime = m_CurFrameTime ;
m_iPrevL2CacheMiss = m_iCurL2CacheMiss ;
# ifdef _X360
m_iPrevLoadHitStores = m_iCurLoadHitStores ;
# endif
m_nTotalCalls + = m_nCurFrameCalls ;
m_TotalTime + = m_CurFrameTime ;
if ( m_PeakTime . IsLessThan ( m_CurFrameTime ) )
{
m_PeakTime = m_CurFrameTime ;
}
m_CurFrameTime . Init ( ) ;
m_nCurFrameCalls = 0 ;
m_iTotalL2CacheMiss + = m_iCurL2CacheMiss ;
m_iCurL2CacheMiss = 0 ;
# ifdef _X360
m_iTotalLoadHitStores + = m_iCurLoadHitStores ;
m_iCurLoadHitStores = 0 ;
# endif
if ( m_pChild )
{
m_pChild - > MarkFrame ( ) ;
}
if ( m_pSibling )
{
m_pSibling - > MarkFrame ( ) ;
}
}
//-------------------------------------
void CVProfNode : : ResetPeak ( )
{
m_PeakTime . Init ( ) ;
if ( m_pChild )
{
m_pChild - > ResetPeak ( ) ;
}
if ( m_pSibling )
{
m_pSibling - > ResetPeak ( ) ;
}
}
void CVProfNode : : SetCurFrameTime ( unsigned long milliseconds )
{
m_CurFrameTime . Init ( ( float ) milliseconds ) ;
}
# ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Ensure that all of our internal structures are consistent, and
// account for all memory that we've allocated.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CVProfNode : : Validate ( CValidator & validator , tchar * pchName )
{
validator . Push ( _T ( " CVProfNode " ) , this , pchName ) ;
m_L2Cache . Validate ( validator , _T ( " m_L2Cache " ) ) ;
if ( m_pSibling )
m_pSibling - > Validate ( validator , _T ( " m_pSibling " ) ) ;
if ( m_pChild )
m_pChild - > Validate ( validator , _T ( " m_pChild " ) ) ;
validator . Pop ( ) ;
}
# endif // DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
struct TimeSums_t
{
const tchar * pszProfileScope ;
int calls ;
double time ;
double timeLessChildren ;
double peak ;
} ;
static bool TimeCompare ( const TimeSums_t & lhs , const TimeSums_t & rhs )
{
return ( lhs . time > rhs . time ) ;
}
static bool TimeLessChildrenCompare ( const TimeSums_t & lhs , const TimeSums_t & rhs )
{
return ( lhs . timeLessChildren > rhs . timeLessChildren ) ;
}
static bool PeakCompare ( const TimeSums_t & lhs , const TimeSums_t & rhs )
{
return ( lhs . peak > rhs . peak ) ;
}
static bool AverageTimeCompare ( const TimeSums_t & lhs , const TimeSums_t & rhs )
{
double avgLhs = ( lhs . calls ) ? lhs . time / ( double ) lhs . calls : 0.0 ;
double avgRhs = ( rhs . calls ) ? rhs . time / ( double ) rhs . calls : 0.0 ;
return ( avgLhs > avgRhs ) ;
}
static bool AverageTimeLessChildrenCompare ( const TimeSums_t & lhs , const TimeSums_t & rhs )
{
double avgLhs = ( lhs . calls ) ? lhs . timeLessChildren / ( double ) lhs . calls : 0.0 ;
double avgRhs = ( rhs . calls ) ? rhs . timeLessChildren / ( double ) rhs . calls : 0.0 ;
return ( avgLhs > avgRhs ) ;
}
static bool PeakOverAverageCompare ( const TimeSums_t & lhs , const TimeSums_t & rhs )
{
double avgLhs = ( lhs . calls ) ? lhs . time / ( double ) lhs . calls : 0.0 ;
double avgRhs = ( rhs . calls ) ? rhs . time / ( double ) rhs . calls : 0.0 ;
double lhsPoA = ( avgLhs ! = 0 ) ? lhs . peak / avgLhs : 0.0 ;
double rhsPoA = ( avgRhs ! = 0 ) ? rhs . peak / avgRhs : 0.0 ;
return ( lhsPoA > rhsPoA ) ;
}
map < CVProfNode * , double > g_TimesLessChildren ;
int g_TotalFrames ;
map < const tchar * , size_t > g_TimeSumsMap ;
vector < TimeSums_t > g_TimeSums ;
CVProfNode * g_pStartNode ;
const tchar * g_pszSumNode ;
//-------------------------------------
void CVProfile : : SumTimes ( CVProfNode * pNode , int budgetGroupID )
{
if ( ! pNode )
return ; // this generally only happens on a failed FindNode()
bool bSetStartNode ;
if ( ! g_pStartNode & & _tcscmp ( pNode - > GetName ( ) , g_pszSumNode ) = = 0 )
{
g_pStartNode = pNode ;
bSetStartNode = true ;
}
else
bSetStartNode = false ;
if ( GetRoot ( ) ! = pNode )
{
if ( g_pStartNode & & pNode - > GetTotalCalls ( ) > 0 & & ( budgetGroupID = = - 1 | | pNode - > GetBudgetGroupID ( ) = = budgetGroupID ) )
{
double timeLessChildren = pNode - > GetTotalTimeLessChildren ( ) ;
g_TimesLessChildren . insert ( make_pair ( pNode , timeLessChildren ) ) ;
map < const tchar * , size_t > : : iterator iter ;
iter = g_TimeSumsMap . find ( pNode - > GetName ( ) ) ; // intenionally using address of string rather than string compare (toml 01-27-03)
if ( iter = = g_TimeSumsMap . end ( ) )
{
TimeSums_t timeSums = { pNode - > GetName ( ) , pNode - > GetTotalCalls ( ) , pNode - > GetTotalTime ( ) , timeLessChildren , pNode - > GetPeakTime ( ) } ;
g_TimeSumsMap . insert ( make_pair ( pNode - > GetName ( ) , g_TimeSums . size ( ) ) ) ;
g_TimeSums . push_back ( timeSums ) ;
}
else
{
TimeSums_t & timeSums = g_TimeSums [ iter - > second ] ;
timeSums . calls + = pNode - > GetTotalCalls ( ) ;
timeSums . time + = pNode - > GetTotalTime ( ) ;
timeSums . timeLessChildren + = timeLessChildren ;
if ( pNode - > GetPeakTime ( ) > timeSums . peak )
timeSums . peak = pNode - > GetPeakTime ( ) ;
}
}
if ( ( ! g_pStartNode | | pNode ! = g_pStartNode ) & & pNode - > GetSibling ( ) )
{
SumTimes ( pNode - > GetSibling ( ) , budgetGroupID ) ;
}
}
if ( pNode - > GetChild ( ) )
{
SumTimes ( pNode - > GetChild ( ) , budgetGroupID ) ;
}
if ( bSetStartNode )
g_pStartNode = NULL ;
}
//-------------------------------------
CVProfNode * CVProfile : : FindNode ( CVProfNode * pStartNode , const tchar * pszNode )
{
if ( _tcscmp ( pStartNode - > GetName ( ) , pszNode ) ! = 0 )
{
CVProfNode * pFoundNode = NULL ;
if ( pStartNode - > GetSibling ( ) )
{
pFoundNode = FindNode ( pStartNode - > GetSibling ( ) , pszNode ) ;
}
if ( ! pFoundNode & & pStartNode - > GetChild ( ) )
{
pFoundNode = FindNode ( pStartNode - > GetChild ( ) , pszNode ) ;
}
return pFoundNode ;
}
return pStartNode ;
}
//-------------------------------------
# ifdef _X360
void CVProfile : : PMCDisableAllNodes ( CVProfNode * pStartNode )
{
if ( pStartNode = = NULL )
{
pStartNode = GetRoot ( ) ;
}
pStartNode - > EnableL2andLHS ( false ) ;
if ( pStartNode - > GetSibling ( ) )
{
PMCDisableAllNodes ( pStartNode - > GetSibling ( ) ) ;
}
if ( pStartNode - > GetChild ( ) )
{
PMCDisableAllNodes ( pStartNode - > GetChild ( ) ) ;
}
}
// recursively set l2/lhs recording state for a node and all children AND SIBLINGS
static void PMCRecursiveL2Set ( CVProfNode * pNode , bool enableState )
{
if ( pNode )
{
pNode - > EnableL2andLHS ( enableState ) ;
if ( pNode - > GetSibling ( ) )
{
PMCRecursiveL2Set ( pNode - > GetSibling ( ) , enableState ) ;
}
if ( pNode - > GetChild ( ) )
{
PMCRecursiveL2Set ( pNode - > GetChild ( ) , enableState ) ;
}
}
}
bool CVProfile : : PMCEnableL2Upon ( const tchar * pszNodeName , bool bRecursive )
{
// PMCDisableAllNodes();
CVProfNode * pNode = FindNode ( GetRoot ( ) , pszNodeName ) ;
if ( pNode )
{
pNode - > EnableL2andLHS ( true ) ;
if ( bRecursive )
{
PMCRecursiveL2Set ( pNode - > GetChild ( ) , true ) ;
}
return true ;
}
else
{
return false ;
}
}
bool CVProfile : : PMCDisableL2Upon ( const tchar * pszNodeName , bool bRecursive )
{
// PMCDisableAllNodes();
CVProfNode * pNode = FindNode ( GetRoot ( ) , pszNodeName ) ;
if ( pNode )
{
pNode - > EnableL2andLHS ( false ) ;
if ( bRecursive )
{
PMCRecursiveL2Set ( pNode - > GetChild ( ) , false ) ;
}
return true ;
}
else
{
return false ;
}
}
static void DumpEnabledPMCNodesInner ( CVProfNode * pNode )
{
if ( ! pNode )
return ;
if ( pNode - > IsL2andLHSEnabled ( ) )
{
Msg ( _T ( " \t %s \n " ) , pNode - > GetName ( ) ) ;
}
// depth first printing clearer
if ( pNode - > GetChild ( ) )
{
DumpEnabledPMCNodesInner ( pNode - > GetChild ( ) ) ;
}
if ( pNode - > GetSibling ( ) )
{
DumpEnabledPMCNodesInner ( pNode - > GetChild ( ) ) ;
}
}
void CVProfile : : DumpEnabledPMCNodes ( void )
{
Msg ( _T ( " Nodes enabled for PMC counters: \n " ) ) ;
CVProfNode * pNode = GetRoot ( ) ;
DumpEnabledPMCNodesInner ( pNode ) ;
Msg ( _T ( " (end) \n " ) ) ;
}
CVProfNode * CVProfile : : CPUTraceGetEnabledNode ( CVProfNode * pStartNode )
{
if ( ! pStartNode )
{
pStartNode = GetRoot ( ) ;
}
if ( ( pStartNode - > m_iBitFlags & CVProfNode : : kCPUTrace ) ! = 0 )
{
return pStartNode ;
}
if ( pStartNode - > GetSibling ( ) )
{
CVProfNode * retval = CPUTraceGetEnabledNode ( pStartNode - > GetSibling ( ) ) ;
if ( retval )
return retval ;
}
if ( pStartNode - > GetChild ( ) )
{
CVProfNode * retval = CPUTraceGetEnabledNode ( pStartNode - > GetChild ( ) ) ;
if ( retval )
return retval ;
}
return NULL ;
}
const char * CVProfile : : SetCPUTraceFilename ( const char * filename )
{
strncpy ( m_CPUTraceFilename , filename , sizeof ( m_CPUTraceFilename ) ) ;
return GetCPUTraceFilename ( ) ;
}
/// Returns a pointer to an internal static, so you don't need to
/// make temporary char buffers for this to write into. What of it?
/// You're not hanging on to that pointer. That would be foolish.
const char * CVProfile : : GetCPUTraceFilename ( )
{
static char retBuf [ 256 ] ;
switch ( m_iCPUTraceEnabled )
{
case kAllNodesInFrame_WaitingForMark :
case kAllNodesInFrame_Recording :
_snprintf ( retBuf , sizeof ( retBuf ) , " e: \\ %.128s%.4d.pix2 " , m_CPUTraceFilename , m_iSuccessiveTraceIndex ) ;
break ;
case kAllNodesInFrame_WaitingForMarkMultiFrame :
case kAllNodesInFrame_RecordingMultiFrame :
_snprintf ( retBuf , sizeof ( retBuf ) , " e: \\ %.128s_%.4d_%.4d.pix2 " , m_CPUTraceFilename , m_nFrameCount , m_iSuccessiveTraceIndex ) ;
break ;
default :
_snprintf ( retBuf , sizeof ( retBuf ) , " e: \\ %.128s.pix2 " , m_CPUTraceFilename ) ;
}
return retBuf ;
}
bool CVProfile : : TraceCompleteEvent ( void )
{
return m_bTraceCompleteEvent ;
}
CVProfNode * CVProfile : : CPUTraceEnableForNode ( const tchar * pszNodeName )
{
// disable whatever may be enabled already (we can only trace one node at a time)
CPUTraceDisableAllNodes ( ) ;
CVProfNode * which = FindNode ( GetRoot ( ) , pszNodeName ) ;
if ( which )
{
which - > m_iBitFlags | = CVProfNode : : kCPUTrace ;
return which ;
}
else
return NULL ;
}
void CVProfile : : CPUTraceDisableAllNodes ( CVProfNode * pStartNode )
{
if ( ! pStartNode )
{
pStartNode = GetRoot ( ) ;
}
pStartNode - > m_iBitFlags & = ~ CVProfNode : : kCPUTrace ;
if ( pStartNode - > GetSibling ( ) )
{
CPUTraceDisableAllNodes ( pStartNode - > GetSibling ( ) ) ;
}
if ( pStartNode - > GetChild ( ) )
{
CPUTraceDisableAllNodes ( pStartNode - > GetChild ( ) ) ;
}
}
# endif
//-------------------------------------
void CVProfile : : SumTimes ( const tchar * pszStartNode , int budgetGroupID )
{
if ( GetRoot ( ) - > GetChild ( ) )
{
if ( pszStartNode = = NULL )
g_pStartNode = GetRoot ( ) ;
else
g_pStartNode = NULL ;
g_pszSumNode = pszStartNode ;
SumTimes ( GetRoot ( ) , budgetGroupID ) ;
g_pStartNode = NULL ;
}
}
//-------------------------------------
// This array lets us generate the commonly used indent levels
// without looping. That then lets us print our vprof nodes
// in a single call, which is more efficient and works better
// with output streams like ETW where each call represents a
// 'line' of text. Indent levels beyond what is represented
// in this array are, regretfully, clamped, however the highest
// indent level seen in testing was 10.
static const char * s_indentText [ ] =
{
" " , // 0
" " , // 1
" | " , // 2
" | | " , // 3
" | | | " , // 4
" | | | | " , // 5
" | | | | | " , // 6
" | | | | | | " , // 7
" | | | | | | | " , // 8
" | | | | | | | | " , // 9
" | | | | | | | | | " , // 10
" | | | | | | | | | | " , // 11
" | | | | | | | | | | | " , // 12
" | | | | | | | | | | | | " , // 13
" | | | | | | | | | | | | | " , // 14
} ;
void CVProfile : : DumpNodes ( CVProfNode * pNode , int indent , bool bAverageAndCountOnly )
{
if ( ! pNode )
return ; // this generally only happens on a failed FindNode()
bool fIsRoot = ( pNode = = GetRoot ( ) ) ;
if ( fIsRoot | | pNode = = g_pStartNode )
{
if ( bAverageAndCountOnly )
{
m_pOutputStream ( _T ( " Avg Time/Frame (ms) \n " ) ) ;
m_pOutputStream ( _T ( " [ func+child func ] Count \n " ) ) ;
m_pOutputStream ( _T ( " ---------- ------ ------ \n " ) ) ;
}
else
{
m_pOutputStream ( _T ( " Sum (ms) Avg Time/Frame (ms) Avg Time/Call (ms) \n " ) ) ;
m_pOutputStream ( _T ( " [ func+child func ] [ func+child func ] [ func+child func ] Count Peak \n " ) ) ;
m_pOutputStream ( _T ( " ---------- ------ ---------- ------ ---------- ------ ------ ------ \n " ) ) ;
}
}
if ( ! fIsRoot )
{
map < CVProfNode * , double > : : iterator iterTimeLessChildren = g_TimesLessChildren . find ( pNode ) ;
indent = Max ( indent , 0 ) ;
indent = Min ( indent , ( int ) ARRAYSIZE ( s_indentText ) - 1 ) ;
const char * indentText = s_indentText [ indent ] ;
double dNodeTime = 0 ;
if ( iterTimeLessChildren ! = g_TimesLessChildren . end ( ) )
dNodeTime = iterTimeLessChildren - > second ;
if ( bAverageAndCountOnly )
{
m_pOutputStream ( _T ( " %10.3f %6.2f %6d %s%s \n " ) ,
( pNode - > GetTotalCalls ( ) > 0 ) ? pNode - > GetTotalTime ( ) / ( double ) NumFramesSampled ( ) : 0 ,
( pNode - > GetTotalCalls ( ) > 0 ) ? dNodeTime / ( double ) NumFramesSampled ( ) : 0 ,
pNode - > GetTotalCalls ( ) , indentText , pNode - > GetName ( ) ) ;
}
else
{
m_pOutputStream ( _T ( " %10.3f %6.2f %10.3f %6.2f %10.3f %6.2f %6d %6.2f %s%s \n " ) ,
pNode - > GetTotalTime ( ) , dNodeTime ,
( pNode - > GetTotalCalls ( ) > 0 ) ? pNode - > GetTotalTime ( ) / ( double ) NumFramesSampled ( ) : 0 ,
( pNode - > GetTotalCalls ( ) > 0 ) ? dNodeTime / ( double ) NumFramesSampled ( ) : 0 ,
( pNode - > GetTotalCalls ( ) > 0 ) ? pNode - > GetTotalTime ( ) / ( double ) pNode - > GetTotalCalls ( ) : 0 ,
( pNode - > GetTotalCalls ( ) > 0 ) ? dNodeTime / ( double ) pNode - > GetTotalCalls ( ) : 0 ,
pNode - > GetTotalCalls ( ) , pNode - > GetPeakTime ( ) , indentText , pNode - > GetName ( ) ) ;
}
}
if ( pNode - > GetChild ( ) )
{
DumpNodes ( pNode - > GetChild ( ) , indent + 1 , bAverageAndCountOnly ) ;
}
if ( ! ( fIsRoot | | pNode = = g_pStartNode ) & & pNode - > GetSibling ( ) )
{
DumpNodes ( pNode - > GetSibling ( ) , indent , bAverageAndCountOnly ) ;
}
}
//-------------------------------------
# if defined( _X360 )
static void CalcBudgetGroupTimes_Recursive ( CVProfNode * pNode , unsigned int * groupTimes , int numGroups , float flScale )
{
int groupID ;
CVProfNode * nodePtr ;
groupID = pNode - > GetBudgetGroupID ( ) ;
if ( groupID > = numGroups )
{
return ;
}
groupTimes [ groupID ] + = flScale * pNode - > GetPrevTimeLessChildren ( ) ;
nodePtr = pNode - > GetSibling ( ) ;
if ( nodePtr )
{
CalcBudgetGroupTimes_Recursive ( nodePtr , groupTimes , numGroups , flScale ) ;
}
nodePtr = pNode - > GetChild ( ) ;
if ( nodePtr )
{
CalcBudgetGroupTimes_Recursive ( nodePtr , groupTimes , numGroups , flScale ) ;
}
}
static void CalcBudgetGroupL2CacheMisses_Recursive ( CVProfNode * pNode , unsigned int * groupTimes , int numGroups , float flScale )
{
int groupID ;
CVProfNode * nodePtr ;
groupID = pNode - > GetBudgetGroupID ( ) ;
if ( groupID > = numGroups )
{
return ;
}
groupTimes [ groupID ] + = flScale * pNode - > GetPrevL2CacheMissLessChildren ( ) ;
nodePtr = pNode - > GetSibling ( ) ;
if ( nodePtr )
{
CalcBudgetGroupL2CacheMisses_Recursive ( nodePtr , groupTimes , numGroups , flScale ) ;
}
nodePtr = pNode - > GetChild ( ) ;
if ( nodePtr )
{
CalcBudgetGroupL2CacheMisses_Recursive ( nodePtr , groupTimes , numGroups , flScale ) ;
}
}
static void CalcBudgetGroupLHS_Recursive ( CVProfNode * pNode , unsigned int * groupTimes , int numGroups , float flScale )
{
int groupID ;
CVProfNode * nodePtr ;
groupID = pNode - > GetBudgetGroupID ( ) ;
if ( groupID > = numGroups )
{
return ;
}
groupTimes [ groupID ] + = flScale * pNode - > GetPrevLoadHitStoreLessChildren ( ) ;
nodePtr = pNode - > GetSibling ( ) ;
if ( nodePtr )
{
CalcBudgetGroupLHS_Recursive ( nodePtr , groupTimes , numGroups , flScale ) ;
}
nodePtr = pNode - > GetChild ( ) ;
if ( nodePtr )
{
CalcBudgetGroupLHS_Recursive ( nodePtr , groupTimes , numGroups , flScale ) ;
}
}
void CVProfile : : VXConsoleReportMode ( VXConsoleReportMode_t mode )
{
m_ReportMode = mode ;
}
void CVProfile : : VXConsoleReportScale ( VXConsoleReportMode_t mode , float flScale )
{
m_pReportScale [ mode ] = flScale ;
}
//-----------------------------------------------------------------------------
// Send the all the counter attributes once to VXConsole at profiling start
//-----------------------------------------------------------------------------
void CVProfile : : VXProfileStart ( )
{
const char * names [ XBX_MAX_PROFILE_COUNTERS ] ;
unsigned int colors [ XBX_MAX_PROFILE_COUNTERS ] ;
int numGroups ;
int counterGroup ;
const char * pGroupName ;
int i ;
int r , g , b , a ;
// vprof system must be running
if ( m_enabled < = 0 | | ! m_UpdateMode )
{
return ;
}
if ( m_UpdateMode & VPROF_UPDATE_BUDGET )
{
// update budget profiling
numGroups = g_VProfCurrentProfile . GetNumBudgetGroups ( ) ;
if ( numGroups > XBX_MAX_PROFILE_COUNTERS )
{
numGroups = XBX_MAX_PROFILE_COUNTERS ;
}
for ( i = 0 ; i < numGroups ; i + + )
{
names [ i ] = g_VProfCurrentProfile . GetBudgetGroupName ( i ) ;
g_VProfCurrentProfile . GetBudgetGroupColor ( i , r , g , b , a ) ;
colors [ i ] = XMAKECOLOR ( r , g , b ) ;
}
// send all the profile attributes
XBX_rSetProfileAttributes ( " cpu " , numGroups , names , colors ) ;
}
if ( m_UpdateMode & ( VPROF_UPDATE_TEXTURE_GLOBAL | VPROF_UPDATE_TEXTURE_PERFRAME ) )
{
// update texture profiling
numGroups = 0 ;
counterGroup = ( m_UpdateMode & VPROF_UPDATE_TEXTURE_GLOBAL ) ? COUNTER_GROUP_TEXTURE_GLOBAL : COUNTER_GROUP_TEXTURE_PER_FRAME ;
for ( i = 0 ; i < g_VProfCurrentProfile . GetNumCounters ( ) ; i + + )
{
if ( g_VProfCurrentProfile . GetCounterGroup ( i ) = = counterGroup )
{
// strip undesired prefix
pGroupName = g_VProfCurrentProfile . GetCounterName ( i ) ;
if ( ! strnicmp ( pGroupName , " texgroup_frame_ " , 15 ) )
{
pGroupName + = 15 ;
}
else if ( ! strnicmp ( pGroupName , " texgroup_global_ " , 16 ) )
{
pGroupName + = 16 ;
}
names [ numGroups ] = pGroupName ;
g_VProfCurrentProfile . GetBudgetGroupColor ( numGroups , r , g , b , a ) ;
colors [ numGroups ] = XMAKECOLOR ( r , g , b ) ;
numGroups + + ;
if ( numGroups = = XBX_MAX_PROFILE_COUNTERS )
{
break ;
}
}
}
// send all the profile attributes
XBX_rSetProfileAttributes ( " texture " , numGroups , names , colors ) ;
}
}
//-----------------------------------------------------------------------------
// Send the counters to VXConsole
//-----------------------------------------------------------------------------
void CVProfile : : VXProfileUpdate ( )
{
int i ;
int counterGroup ;
int numGroups ;
unsigned int groupData [ XBX_MAX_PROFILE_COUNTERS ] ;
// vprof system must be running
if ( m_enabled < = 0 | | ! m_UpdateMode )
{
return ;
}
if ( m_UpdateMode & VPROF_UPDATE_BUDGET )
{
// send the cpu counters
numGroups = g_VProfCurrentProfile . GetNumBudgetGroups ( ) ;
if ( numGroups > XBX_MAX_PROFILE_COUNTERS )
{
numGroups = XBX_MAX_PROFILE_COUNTERS ;
}
memset ( groupData , 0 , numGroups * sizeof ( unsigned int ) ) ;
CVProfNode * pNode = g_VProfCurrentProfile . GetRoot ( ) ;
if ( pNode & & pNode - > GetChild ( ) )
{
switch ( m_ReportMode )
{
default :
case VXCONSOLE_REPORT_TIME :
CalcBudgetGroupTimes_Recursive ( pNode - > GetChild ( ) , groupData , numGroups , m_pReportScale [ VXCONSOLE_REPORT_TIME ] ) ;
break ;
case VXCONSOLE_REPORT_L2CACHE_MISSES :
CalcBudgetGroupL2CacheMisses_Recursive ( pNode - > GetChild ( ) , groupData , numGroups , m_pReportScale [ VXCONSOLE_REPORT_L2CACHE_MISSES ] ) ;
break ;
case VXCONSOLE_REPORT_LOAD_HIT_STORE :
CalcBudgetGroupLHS_Recursive ( pNode - > GetChild ( ) , groupData , numGroups , m_pReportScale [ VXCONSOLE_REPORT_LOAD_HIT_STORE ] ) ;
break ;
}
}
XBX_rSetProfileData ( " cpu " , numGroups , groupData ) ;
}
if ( m_UpdateMode & ( VPROF_UPDATE_TEXTURE_GLOBAL | VPROF_UPDATE_TEXTURE_PERFRAME ) )
{
// send the texture counters
numGroups = 0 ;
counterGroup = ( m_UpdateMode & VPROF_UPDATE_TEXTURE_GLOBAL ) ? COUNTER_GROUP_TEXTURE_GLOBAL : COUNTER_GROUP_TEXTURE_PER_FRAME ;
for ( i = 0 ; i < g_VProfCurrentProfile . GetNumCounters ( ) ; i + + )
{
if ( g_VProfCurrentProfile . GetCounterGroup ( i ) = = counterGroup )
{
// get the size in bytes
groupData [ numGroups + + ] = g_VProfCurrentProfile . GetCounterValue ( i ) ;
if ( numGroups = = XBX_MAX_PROFILE_COUNTERS )
{
break ;
}
}
}
XBX_rSetProfileData ( " texture " , numGroups , groupData ) ;
}
}
void CVProfile : : VXEnableUpdateMode ( int event , bool bEnable )
{
// enable or disable the updating of specified events
if ( bEnable )
{
m_UpdateMode | = event ;
}
else
{
m_UpdateMode & = ~ event ;
}
// force a resend of possibly affected attributes
VXProfileStart ( ) ;
}
# define MAX_VPROF_NODES_IN_LIST 4096
static void VXBuildNodeList_r ( CVProfNode * pNode , xVProfNodeItem_t * pNodeList , int * pNumNodes )
{
if ( ! pNode )
{
return ;
}
if ( * pNumNodes > = MAX_VPROF_NODES_IN_LIST )
{
// list full
return ;
}
// add to list
pNodeList [ * pNumNodes ] . pName = ( const char * ) pNode - > GetName ( ) ;
pNodeList [ * pNumNodes ] . pBudgetGroupName = g_VProfCurrentProfile . GetBudgetGroupName ( pNode - > GetBudgetGroupID ( ) ) ;
int r , g , b , a ;
g_VProfCurrentProfile . GetBudgetGroupColor ( pNode - > GetBudgetGroupID ( ) , r , g , b , a ) ;
pNodeList [ * pNumNodes ] . budgetGroupColor = XMAKECOLOR ( r , g , b ) ;
pNodeList [ * pNumNodes ] . totalCalls = pNode - > GetTotalCalls ( ) ;
pNodeList [ * pNumNodes ] . inclusiveTime = pNode - > GetTotalTime ( ) ;
pNodeList [ * pNumNodes ] . exclusiveTime = pNode - > GetTotalTimeLessChildren ( ) ;
( * pNumNodes ) + + ;
CVProfNode * nodePtr = pNode - > GetSibling ( ) ;
if ( nodePtr )
{
VXBuildNodeList_r ( nodePtr , pNodeList , pNumNodes ) ;
}
nodePtr = pNode - > GetChild ( ) ;
if ( nodePtr )
{
VXBuildNodeList_r ( nodePtr , pNodeList , pNumNodes ) ;
}
}
void CVProfile : : VXSendNodes ( void )
{
Pause ( ) ;
xVProfNodeItem_t * pNodeList = ( xVProfNodeItem_t * ) stackalloc ( MAX_VPROF_NODES_IN_LIST * sizeof ( xVProfNodeItem_t ) ) ;
int numNodes = 0 ;
VXBuildNodeList_r ( GetRoot ( ) , pNodeList , & numNodes ) ;
// send to vxconsole
XBX_rVProfNodeList ( numNodes , pNodeList ) ;
Resume ( ) ;
}
# endif
//-------------------------------------
static void DumpSorted ( CVProfile : : StreamOut_t outputStream , const tchar * pszHeading , double totalTime , bool ( * pfnSort ) ( const TimeSums_t & , const TimeSums_t & ) , int maxLen = 999999 )
{
unsigned i ;
vector < TimeSums_t > sortedSums ;
sortedSums = g_TimeSums ;
sort ( sortedSums . begin ( ) , sortedSums . end ( ) , pfnSort ) ;
outputStream ( _T ( " %s \n " ) , pszHeading ) ;
outputStream ( _T ( " Scope Calls Calls/Frame Time+Child Pct Time Pct Avg/Frame Avg/Call Avg-NoChild Peak \n " ) ) ;
outputStream ( _T ( " ---------------------------------------------------- ----------- ----------- ----------- ------ ----------- ------ ----------- ----------- ----------- ----------- \n " ) ) ;
for ( i = 0 ; i < sortedSums . size ( ) & & i < ( unsigned ) maxLen ; i + + )
{
double avg = ( sortedSums [ i ] . calls ) ? sortedSums [ i ] . time / ( double ) sortedSums [ i ] . calls : 0.0 ;
double avgLessChildren = ( sortedSums [ i ] . calls ) ? sortedSums [ i ] . timeLessChildren / ( double ) sortedSums [ i ] . calls : 0.0 ;
outputStream ( _T ( " %52.52s%12d%12.3f%12.3f%7.2f%12.3f%7.2f%12.3f%12.3f%12.3f%12.3f \n " ) ,
sortedSums [ i ] . pszProfileScope ,
sortedSums [ i ] . calls ,
( float ) sortedSums [ i ] . calls / ( float ) g_TotalFrames ,
sortedSums [ i ] . time ,
min ( ( sortedSums [ i ] . time / totalTime ) * 100.0 , 100.0 ) ,
sortedSums [ i ] . timeLessChildren ,
min ( ( sortedSums [ i ] . timeLessChildren / totalTime ) * 100.0 , 100.0 ) ,
sortedSums [ i ] . time / ( float ) g_TotalFrames ,
avg ,
avgLessChildren ,
sortedSums [ i ] . peak ) ;
}
}
# if _X360
// Dump information on all nodes with PMC recording
static void DumpPMC ( CVProfNode * pNode , bool & bPrintHeader , uint64 L2thresh = 1 , uint64 LHSthresh = 1 )
{
if ( ! pNode ) return ;
uint64 l2 = pNode - > GetL2CacheMisses ( ) ;
uint64 lhs = pNode - > GetLoadHitStores ( ) ;
if ( l2 > L2thresh & &
lhs > LHSthresh )
{
// met threshold.
if ( bPrintHeader )
{
// print header
Msg ( _T ( " -- 360 PMC information -- \n " ) ) ;
Msg ( _T ( " Scope L2/call L2/frame LHS/call LHS/frame \n " ) ) ;
Msg ( _T ( " ---------------------------------------------------- --------- --------- --------- --------- \n " ) ) ;
bPrintHeader = false ;
}
// print
float calls = pNode - > GetTotalCalls ( ) ;
float frames = g_TotalFrames ;
Msg ( _T ( " %52.52s %9.2f %9.2f %9.2f %9.2f \n " ) , pNode - > GetName ( ) , l2 / calls , l2 / frames , lhs / calls , lhs / frames ) ;
}
if ( pNode - > GetSibling ( ) )
{
DumpPMC ( pNode - > GetSibling ( ) , bPrintHeader , L2thresh , LHSthresh ) ;
}
if ( pNode - > GetChild ( ) )
{
DumpPMC ( pNode - > GetChild ( ) , bPrintHeader , L2thresh , LHSthresh ) ;
}
}
# endif
//-------------------------------------
void CVProfile : : SetOutputStream ( StreamOut_t outputStream )
{
if ( outputStream ! = NULL )
m_pOutputStream = outputStream ;
else
m_pOutputStream = Msg ;
}
//-------------------------------------
void CVProfile : : OutputReport ( int type , const tchar * pszStartNode , int budgetGroupID )
{
m_pOutputStream ( _T ( " ******** BEGIN VPROF REPORT ******** \n " ) ) ;
# ifdef _MSC_VER
# if (_MSC_VER < 1300)
m_pOutputStream ( _T ( " (note: this report exceeds the output capacity of MSVC debug window. Use console window or console log.) \n " ) ) ;
# endif
# endif
g_TotalFrames = max ( NumFramesSampled ( ) - 1 , 1 ) ;
if ( NumFramesSampled ( ) = = 0 | | GetTotalTimeSampled ( ) = = 0 )
m_pOutputStream ( _T ( " No samples \n " ) ) ;
else
{
if ( type & VPRT_SUMMARY )
{
m_pOutputStream ( _T ( " -- Summary -- \n " ) ) ;
m_pOutputStream ( _T ( " %d frames sampled for %.2f seconds \n " ) , g_TotalFrames , GetTotalTimeSampled ( ) / 1000.0 ) ;
m_pOutputStream ( _T ( " Average %.2f fps, %.2f ms per frame \n " ) , 1000.0 / ( GetTotalTimeSampled ( ) / g_TotalFrames ) , GetTotalTimeSampled ( ) / g_TotalFrames ) ;
m_pOutputStream ( _T ( " Peak %.2f ms frame \n " ) , GetPeakFrameTime ( ) ) ;
double timeAccountedFor = 100.0 - ( m_Root . GetTotalTimeLessChildren ( ) / m_Root . GetTotalTime ( ) ) ;
m_pOutputStream ( _T ( " %.0f pct of time accounted for \n " ) , min ( 100.0 , timeAccountedFor ) ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( pszStartNode = = NULL )
{
pszStartNode = GetRoot ( ) - > GetName ( ) ;
}
SumTimes ( pszStartNode , budgetGroupID ) ;
// Dump the hierarchy
if ( type & VPRT_HIERARCHY )
{
m_pOutputStream ( _T ( " -- Hierarchical Call Graph -- \n " ) ) ;
if ( pszStartNode = = NULL )
g_pStartNode = NULL ;
else
g_pStartNode = FindNode ( GetRoot ( ) , pszStartNode ) ;
DumpNodes ( ( ! g_pStartNode ) ? GetRoot ( ) : g_pStartNode , 0 , false ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( type & VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY )
{
m_pOutputStream ( _T ( " -- Hierarchical Call Graph -- \n " ) ) ;
if ( pszStartNode = = NULL )
g_pStartNode = NULL ;
else
g_pStartNode = FindNode ( GetRoot ( ) , pszStartNode ) ;
DumpNodes ( ( ! g_pStartNode ) ? GetRoot ( ) : g_pStartNode , 0 , true ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
int maxLen = ( type & VPRT_LIST_TOP_ITEMS_ONLY ) ? 25 : 999999 ;
if ( type & VPRT_LIST_BY_TIME )
{
DumpSorted ( m_pOutputStream , _T ( " -- Profile scopes sorted by time (including children) -- " ) , GetTotalTimeSampled ( ) , TimeCompare , maxLen ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( type & VPRT_LIST_BY_TIME_LESS_CHILDREN )
{
DumpSorted ( m_pOutputStream , _T ( " -- Profile scopes sorted by time (without children) -- " ) , GetTotalTimeSampled ( ) , TimeLessChildrenCompare , maxLen ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( type & VPRT_LIST_BY_AVG_TIME )
{
DumpSorted ( m_pOutputStream , _T ( " -- Profile scopes sorted by average time (including children) -- " ) , GetTotalTimeSampled ( ) , AverageTimeCompare , maxLen ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( type & VPRT_LIST_BY_AVG_TIME_LESS_CHILDREN )
{
DumpSorted ( m_pOutputStream , _T ( " -- Profile scopes sorted by average time (without children) -- " ) , GetTotalTimeSampled ( ) , AverageTimeLessChildrenCompare , maxLen ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( type & VPRT_LIST_BY_PEAK_TIME )
{
DumpSorted ( m_pOutputStream , _T ( " -- Profile scopes sorted by peak -- " ) , GetTotalTimeSampled ( ) , PeakCompare , maxLen ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
if ( type & VPRT_LIST_BY_PEAK_OVER_AVERAGE )
{
DumpSorted ( m_pOutputStream , _T ( " -- Profile scopes sorted by peak over average (including children) -- " ) , GetTotalTimeSampled ( ) , PeakOverAverageCompare , maxLen ) ;
m_pOutputStream ( _T ( " \n " ) ) ;
}
// TODO: Functions by time less children
// TODO: Functions by time averages
// TODO: Functions by peak
// TODO: Peak deviation from average
g_TimesLessChildren . clear ( ) ;
g_TimeSumsMap . clear ( ) ;
g_TimeSums . clear ( ) ;
# ifdef _X360
bool bPrintedHeader = true ;
DumpPMC ( FindNode ( GetRoot ( ) , pszStartNode ) , bPrintedHeader ) ;
# endif
}
m_pOutputStream ( _T ( " ******** END VPROF REPORT ******** \n " ) ) ;
}
//=============================================================================
CVProfile : : CVProfile ( )
: m_Root ( _T ( " Root " ) , 0 , NULL , VPROF_BUDGETGROUP_OTHER_UNACCOUNTED , 0 ) ,
m_pCurNode ( & m_Root ) ,
m_nFrames ( 0 ) ,
m_enabled ( 0 ) ,
m_pausedEnabledDepth ( 0 ) ,
m_fAtRoot ( true ) ,
m_pOutputStream ( Msg )
{
# ifdef VPROF_VTUNE_GROUP
m_GroupIDStackDepth = 1 ;
m_GroupIDStack [ 0 ] = 0 ; // VPROF_BUDGETGROUP_OTHER_UNACCOUNTED
# endif
m_TargetThreadId = ThreadGetCurrentId ( ) ;
// Go ahead and allocate 32 slots for budget group names
MEM_ALLOC_CREDIT ( ) ;
m_pBudgetGroups = new CVProfile : : CBudgetGroup [ 32 ] ;
m_nBudgetGroupNames = 0 ;
m_nBudgetGroupNamesAllocated = 32 ;
// Add these here so that they will always be in the same order.
// VPROF_BUDGETGROUP_OTHER_UNACCOUNTED has to be FIRST!!!!
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OTHER_UNACCOUNTED , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_WORLD_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_GAME , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_PLAYER , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_NPCS , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_SERVER_ANIM , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_CLIENT_ANIMATION , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_PHYSICS , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_STATICPROP_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_MODEL_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_LIGHTCACHE , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_BRUSHMODEL_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_SHADOW_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_DETAILPROP_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_PARTICLE_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_ROPES , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_DLIGHT_RENDERING , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OTHER_NETWORKING , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OTHER_SOUND , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OTHER_VGUI , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OTHER_FILESYSTEM , BUDGETFLAG_OTHER | BUDGETFLAG_SERVER ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_PREDICTION , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_INTERPOLATION , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_SWAP_BUFFERS , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OCCLUSION , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_OVERLAYS , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_TOOLS , BUDGETFLAG_OTHER | BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_TEXTURE_CACHE , BUDGETFLAG_CLIENT ) ;
BudgetGroupNameToBudgetGroupID ( VPROF_BUDGETGROUP_REPLAY , BUDGETFLAG_SERVER ) ;
// BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DISP_HULLTRACES );
m_bPMEInit = false ;
m_bPMEEnabled = false ;
# ifdef _X360
m_UpdateMode = 0 ;
m_iCPUTraceEnabled = kDisabled ;
m_bTraceCompleteEvent = false ;
m_iSuccessiveTraceIndex = 0 ;
m_ReportMode = VXCONSOLE_REPORT_TIME ;
m_pReportScale [ VXCONSOLE_REPORT_TIME ] = 1000.0f ;
m_pReportScale [ VXCONSOLE_REPORT_L2CACHE_MISSES ] = 1.0f ;
m_pReportScale [ VXCONSOLE_REPORT_LOAD_HIT_STORE ] = 0.1f ;
m_nFrameCount = 0 ;
m_nFramesRemaining = 1 ;
m_WorstCycles = 0 ;
m_WorstTraceFilename [ 0 ] = 0 ;
# endif
}
CVProfile : : ~ CVProfile ( )
{
Term ( ) ;
}
void CVProfile : : FreeNodes_R ( CVProfNode * pNode )
{
CVProfNode * pNext ;
for ( CVProfNode * pChild = pNode - > GetChild ( ) ; pChild ; pChild = pNext )
{
pNext = pChild - > GetSibling ( ) ;
FreeNodes_R ( pChild ) ;
}
if ( pNode = = GetRoot ( ) )
{
pNode - > m_pChild = NULL ;
}
else
{
delete pNode ;
}
}
void CVProfile : : Term ( )
{
int i ;
for ( i = 0 ; i < m_nBudgetGroupNames ; i + + )
{
delete [ ] m_pBudgetGroups [ i ] . m_pName ;
}
delete m_pBudgetGroups ;
m_nBudgetGroupNames = m_nBudgetGroupNamesAllocated = 0 ;
m_pBudgetGroups = NULL ;
int n ;
for ( n = 0 ; n < m_NumCounters ; n + + )
{
delete [ ] m_CounterNames [ n ] ;
m_CounterNames [ n ] = NULL ;
}
m_NumCounters = 0 ;
// Free the nodes.
if ( GetRoot ( ) )
{
FreeNodes_R ( GetRoot ( ) ) ;
}
}
# define COLORMIN 160
# define COLORMAX 255
static int g_ColorLookup [ 4 ] =
{
COLORMIN ,
COLORMAX ,
COLORMIN + ( COLORMAX - COLORMIN ) / 3 ,
COLORMIN + ( ( COLORMAX - COLORMIN ) * 2 ) / 3 ,
} ;
# define GET_BIT( val, bitnum ) ( ( val >> bitnum ) & 0x1 )
void CVProfile : : GetBudgetGroupColor ( int budgetGroupID , int & r , int & g , int & b , int & a )
{
budgetGroupID = budgetGroupID % ( 1 < < 6 ) ;
int index ;
index = GET_BIT ( budgetGroupID , 0 ) | ( GET_BIT ( budgetGroupID , 5 ) < < 1 ) ;
r = g_ColorLookup [ index ] ;
index = GET_BIT ( budgetGroupID , 1 ) | ( GET_BIT ( budgetGroupID , 4 ) < < 1 ) ;
g = g_ColorLookup [ index ] ;
index = GET_BIT ( budgetGroupID , 2 ) | ( GET_BIT ( budgetGroupID , 3 ) < < 1 ) ;
b = g_ColorLookup [ index ] ;
a = 255 ;
}
// return -1 if it doesn't exist.
int CVProfile : : FindBudgetGroupName ( const tchar * pBudgetGroupName )
{
int i ;
for ( i = 0 ; i < m_nBudgetGroupNames ; i + + )
{
if ( _tcsicmp ( pBudgetGroupName , m_pBudgetGroups [ i ] . m_pName ) = = 0 )
{
return i ;
}
}
return - 1 ;
}
int CVProfile : : AddBudgetGroupName ( const tchar * pBudgetGroupName , int budgetFlags )
{
MEM_ALLOC_CREDIT ( ) ;
tchar * pNewString = new tchar [ _tcslen ( pBudgetGroupName ) + 1 ] ;
_tcscpy ( pNewString , pBudgetGroupName ) ;
if ( m_nBudgetGroupNames + 1 > m_nBudgetGroupNamesAllocated )
{
m_nBudgetGroupNamesAllocated * = 2 ;
m_nBudgetGroupNamesAllocated = max ( m_nBudgetGroupNames + 6 , m_nBudgetGroupNamesAllocated ) ;
CBudgetGroup * pNew = new CBudgetGroup [ m_nBudgetGroupNamesAllocated ] ;
for ( int i = 0 ; i < m_nBudgetGroupNames ; i + + )
pNew [ i ] = m_pBudgetGroups [ i ] ;
delete [ ] m_pBudgetGroups ;
m_pBudgetGroups = pNew ;
}
m_pBudgetGroups [ m_nBudgetGroupNames ] . m_pName = pNewString ;
m_pBudgetGroups [ m_nBudgetGroupNames ] . m_BudgetFlags = budgetFlags ;
m_nBudgetGroupNames + + ;
if ( m_pNumBudgetGroupsChangedCallBack )
{
( * m_pNumBudgetGroupsChangedCallBack ) ( ) ;
}
# if defined( _X360 )
// re-start with all the known budgets
VXProfileStart ( ) ;
# endif
return m_nBudgetGroupNames - 1 ;
}
int CVProfile : : BudgetGroupNameToBudgetGroupID ( const tchar * pBudgetGroupName , int budgetFlagsToORIn )
{
int budgetGroupID = FindBudgetGroupName ( pBudgetGroupName ) ;
if ( budgetGroupID = = - 1 )
{
budgetGroupID = AddBudgetGroupName ( pBudgetGroupName , budgetFlagsToORIn ) ;
}
else
{
m_pBudgetGroups [ budgetGroupID ] . m_BudgetFlags | = budgetFlagsToORIn ;
}
return budgetGroupID ;
}
int CVProfile : : BudgetGroupNameToBudgetGroupID ( const tchar * pBudgetGroupName )
{
return BudgetGroupNameToBudgetGroupID ( pBudgetGroupName , BUDGETFLAG_OTHER ) ;
}
int CVProfile : : GetNumBudgetGroups ( void )
{
return m_nBudgetGroupNames ;
}
void CVProfile : : RegisterNumBudgetGroupsChangedCallBack ( void ( * pCallBack ) ( void ) )
{
m_pNumBudgetGroupsChangedCallBack = pCallBack ;
}
void CVProfile : : HideBudgetGroup ( int budgetGroupID , bool bHide )
{
if ( budgetGroupID ! = - 1 )
{
if ( bHide )
m_pBudgetGroups [ budgetGroupID ] . m_BudgetFlags | = BUDGETFLAG_HIDDEN ;
else
m_pBudgetGroups [ budgetGroupID ] . m_BudgetFlags & = ~ BUDGETFLAG_HIDDEN ;
}
}
int * CVProfile : : FindOrCreateCounter ( const tchar * pName , CounterGroup_t eCounterGroup )
{
Assert ( m_NumCounters + 1 < MAXCOUNTERS ) ;
if ( m_NumCounters + 1 > = MAXCOUNTERS | | ! InTargetThread ( ) )
{
static int dummy ;
return & dummy ;
}
int i ;
for ( i = 0 ; i < m_NumCounters ; i + + )
{
if ( _tcsicmp ( m_CounterNames [ i ] , pName ) = = 0 )
{
// found it!
return & m_Counters [ i ] ;
}
}
// NOTE: These get freed in ~CVProfile.
MEM_ALLOC_CREDIT ( ) ;
tchar * pNewName = new tchar [ _tcslen ( pName ) + 1 ] ;
_tcscpy ( pNewName , pName ) ;
m_Counters [ m_NumCounters ] = 0 ;
m_CounterGroups [ m_NumCounters ] = ( char ) eCounterGroup ;
m_CounterNames [ m_NumCounters + + ] = pNewName ;
return & m_Counters [ m_NumCounters - 1 ] ;
}
void CVProfile : : ResetCounters ( CounterGroup_t eCounterGroup )
{
int i ;
for ( i = 0 ; i < m_NumCounters ; i + + )
{
if ( m_CounterGroups [ i ] = = eCounterGroup )
m_Counters [ i ] = 0 ;
}
}
int CVProfile : : GetNumCounters ( ) const
{
return m_NumCounters ;
}
const tchar * CVProfile : : GetCounterName ( int index ) const
{
Assert ( index > = 0 & & index < m_NumCounters ) ;
return m_CounterNames [ index ] ;
}
int CVProfile : : GetCounterValue ( int index ) const
{
Assert ( index > = 0 & & index < m_NumCounters ) ;
return m_Counters [ index ] ;
}
const tchar * CVProfile : : GetCounterNameAndValue ( int index , int & val ) const
{
Assert ( index > = 0 & & index < m_NumCounters ) ;
val = m_Counters [ index ] ;
return m_CounterNames [ index ] ;
}
CounterGroup_t CVProfile : : GetCounterGroup ( int index ) const
{
Assert ( index > = 0 & & index < m_NumCounters ) ;
return ( CounterGroup_t ) m_CounterGroups [ index ] ;
}
# ifdef _X360
void CVProfile : : LatchMultiFrame ( int64 cycles )
{
if ( cycles > m_WorstCycles )
{
strncpy ( m_WorstTraceFilename , GetCPUTraceFilename ( ) , sizeof ( m_WorstTraceFilename ) ) ;
m_WorstCycles = cycles ;
}
}
void CVProfile : : SpewWorstMultiFrame ( )
{
CCycleCount cc ( m_WorstCycles ) ;
m_pOutputStream ( " %s == %.3f msec \n " , m_WorstTraceFilename , cc . GetMillisecondsF ( ) ) ;
}
# endif
# ifdef DBGFLAG_VALIDATE
# ifdef _WIN64
# error the below is presumably broken on 64 bit
# endif // _WIN64
const int k_cSTLMapAllocOffset = 4 ;
# define GET_INTERNAL_MAP_ALLOC_PTR( pMap ) \
( * ( ( void * * ) ( ( ( byte * ) ( pMap ) ) + k_cSTLMapAllocOffset ) ) )
//-----------------------------------------------------------------------------
// Purpose: Ensure that all of our internal structures are consistent, and
// account for all memory that we've allocated.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CVProfile : : Validate ( CValidator & validator , tchar * pchName )
{
validator . Push ( _T ( " CVProfile " ) , this , pchName ) ;
m_Root . Validate ( validator , _T ( " m_Root " ) ) ;
for ( int iBudgetGroup = 0 ; iBudgetGroup < m_nBudgetGroupNames ; iBudgetGroup + + )
validator . ClaimMemory ( m_pBudgetGroups [ iBudgetGroup ] . m_pName ) ;
validator . ClaimMemory ( m_pBudgetGroups ) ;
// The std template map class allocates memory internally, but offer no way to get
// access to their pointer. Since this is for debug purposes only and the
// std template classes don't change, just look at the well-known offset. This
// is arguably sick and wrong, kind of like marrying a squirrel.
validator . ClaimMemory ( GET_INTERNAL_MAP_ALLOC_PTR ( & g_TimesLessChildren ) ) ;
validator . ClaimMemory ( GET_INTERNAL_MAP_ALLOC_PTR ( & g_TimeSumsMap ) ) ;
validator . Pop ( ) ;
}
# endif // DBGFLAG_VALIDATE
# endif // VPROF_ENABLED
# ifdef RAD_TELEMETRY_ENABLED
TelemetryData g_Telemetry ;
static HTELEMETRY g_tmContext ;
static TmU8 * g_pTmMemoryArena = NULL ;
static bool g_TelemetryLoaded = false ;
static unsigned int g_TelemetryFrameCount = 0 ;
static bool g_fTelemetryLevelChanged = false ;
static const TmU32 TELEMETRY_ARENA_SIZE = 8 * 1024 * 1024 ; // How much memory we want Telemetry to use.
struct ThreadNameInfo_t
{
TSLNodeBase_t base ;
ThreadId_t ThreadID ;
char szName [ 64 ] ;
} ;
static CTSSimpleList < ThreadNameInfo_t > g_ThreadNamesList ;
static bool g_bThreadNameArrayChanged = false ;
static int g_ThreadNameArrayCount = 0 ;
static ThreadNameInfo_t * g_ThreadNameArray [ 64 ] ;
void TelemetryThreadSetDebugName ( ThreadId_t id , const char * pszName )
{
ThreadNameInfo_t * pThreadNameInfo = new ThreadNameInfo_t ;
if ( id = = ( uint32 ) - 1 )
{
id = ThreadGetCurrentId ( ) ;
}
pThreadNameInfo - > ThreadID = id ;
strncpy ( pThreadNameInfo - > szName , pszName , ARRAYSIZE ( pThreadNameInfo - > szName ) ) ;
pThreadNameInfo - > szName [ ARRAYSIZE ( pThreadNameInfo - > szName ) - 1 ] = 0 ;
g_ThreadNamesList . Push ( pThreadNameInfo ) ;
g_bThreadNameArrayChanged = true ;
}
static void UpdateTelemetryThreadNames ( )
{
if ( g_bThreadNameArrayChanged )
{
// Go through and add any new thread names we got in our thread safe list to our thread names array.
for ( ThreadNameInfo_t * pThreadNameInfo = g_ThreadNamesList . Pop ( ) ;
pThreadNameInfo ;
pThreadNameInfo = g_ThreadNamesList . Pop ( ) )
{
if ( g_ThreadNameArrayCount < ARRAYSIZE ( g_ThreadNameArray ) )
{
g_ThreadNameArray [ g_ThreadNameArrayCount ] = pThreadNameInfo ;
g_ThreadNameArrayCount + + ;
}
else
{
delete pThreadNameInfo ;
}
}
tmThreadName ( g_tmContext , ThreadGetCurrentId ( ) , " MainThrd " ) ;
for ( int i = 0 ; i < g_ThreadNameArrayCount ; i + + )
{
tmThreadName ( g_tmContext , g_ThreadNameArray [ i ] - > ThreadID , g_ThreadNameArray [ i ] - > szName ) ;
}
g_bThreadNameArrayChanged = false ;
}
}
static bool TelemetryInitialize ( )
{
if ( g_tmContext )
{
//TmConnectionStatus status = tmGetConnectionStatus( g_tmContext );
TmConnectionStatus status = TmConnectionStatus : : TMCS_DISCONNECTED ;
if ( status = = TMCS_CONNECTED | | status = = TMCS_CONNECTING )
return true ;
}
TmErrorCode retVal ;
if ( ! g_TelemetryLoaded )
{
// Pass in 0 if you want to use the release mode DLL or 1 if you want to
// use the checked DLL. The checked DLL is compiled with optimizations but
// does extra run time checks and reporting.
//int nLoadTelemetry = tmLoadTelemetry( 0 );
int nLoadTelemetry = 0 ;
//retVal = tmStartup();
retVal = 0 ;
if ( retVal ! = TM_OK )
{
Warning ( " TelemetryInit() failed: tmStartup() returned %d, tmLoadTelemetry() returned %d. \n " , retVal , nLoadTelemetry ) ;
return false ;
}
if ( ! g_pTmMemoryArena )
{
g_pTmMemoryArena = new TmU8 [ TELEMETRY_ARENA_SIZE ] ;
}
//retVal = tmInitializeContext( &g_tmContext, g_pTmMemoryArena, TELEMETRY_ARENA_SIZE );
retVal = 0 ;
if ( retVal ! = TM_OK )
{
delete [ ] g_pTmMemoryArena ;
g_pTmMemoryArena = NULL ;
Warning ( " TelemetryInit() failed: tmInitializeContext() returned %d. \n " , retVal ) ;
return false ;
}
g_TelemetryLoaded = true ;
}
const char * pGameName = " tf2 " ;
# if defined( IS_WINDOWS_PC )
char baseExeFilename [ 512 ] ;
if ( GetModuleFileName ( GetModuleHandle ( NULL ) , baseExeFilename , sizeof ( baseExeFilename ) ) )
{
char * pExt = strrchr ( baseExeFilename , ' . ' ) ;
if ( pExt )
* pExt = 0 ;
char * pSeparator = strrchr ( baseExeFilename , ' \\ ' ) ;
pGameName = pSeparator ? ( pSeparator + 1 ) : baseExeFilename ;
}
// If you've got \\perforce\symbols on your _NT_SYMBOL_PATH, tmOpen() can take a massively long
// time in the symInitialize() routine. Since we don't really need that, kill it here.
putenv ( " _NT_SYMBOL_PATH= " ) ;
# endif
const char * pServerAddress = g_Telemetry . ServerAddress [ 0 ] ? g_Telemetry . ServerAddress : " localhost " ;
TmConnectionType tmType = ! V_tier0_stricmp ( pServerAddress , " FILE " ) ? TMCT_FILE : TMCT_TCP ;
Msg ( " TELEMETRY: Calling tmOpen( %s )... \n " , pServerAddress ) ;
char szBuildInfo [ 2048 ] ;
_snprintf ( szBuildInfo , ARRAYSIZE ( szBuildInfo ) , " %s: %s " , __DATE__ __TIME__ , Plat_GetCommandLineA ( ) ) ;
szBuildInfo [ ARRAYSIZE ( szBuildInfo ) - 1 ] = 0 ;
TmU32 TmOpenFlags = TMOF_DEFAULT | TMOF_MINIMAL_CONTEXT_SWITCHES ;
/* TmOpenFlags |= TMOF_DISABLE_CONTEXT_SWITCHES | TMOF_INIT_NETWORKING*/
//retVal = tmOpen( g_tmContext, pGameName, szBuildInfo, pServerAddress, tmType,
// TELEMETRY_DEFAULT_PORT, TmOpenFlags, 1000 );
retVal = 0 ;
if ( retVal ! = TM_OK )
{
Warning ( " TelemetryInitialize() failed: tmOpen returned %d. \n " , retVal ) ;
return false ;
}
Msg ( " Telemetry initialized at level %u. \n " , g_Telemetry . Level ) ;
// Make sure we set all the thread names.
g_bThreadNameArrayChanged = true ;
UpdateTelemetryThreadNames ( ) ;
return true ;
}
static void TelemetryShutdown ( bool InDtor = false )
{
if ( g_tmContext )
{
// Msg can't be called here as tier0 may have already been shut down...
if ( ! InDtor )
{
Msg ( " Shutting down telemetry. \n " ) ;
}
//TmConnectionStatus status = tmGetConnectionStatus( g_tmContext );
TmConnectionStatus status = 0 ;
if ( status = = TMCS_CONNECTED | | status = = TMCS_CONNECTING )
tmClose ( g_tmContext ) ;
// Discontinue new usage of the context before shutting it down (multithreading).
memset ( g_Telemetry . tmContext , 0 , sizeof ( g_Telemetry . tmContext ) ) ;
HTELEMETRY hShutdown = g_tmContext ;
g_tmContext = NULL ;
//tmShutdownContext( hShutdown );
//tmShutdown();
g_TelemetryLoaded = false ;
}
}
// Helper class to initialize Telemetry.
class CTelemetryRegister
{
public :
CTelemetryRegister ( ) { }
~ CTelemetryRegister ( ) { TelemetryShutdown ( true ) ; }
} g_TelemetryRegister ;
PLATFORM_INTERFACE void TelemetrySetLevel ( unsigned int Level )
{
if ( Level ! = g_Telemetry . Level )
{
DevMsg ( " TelemetrySetLevel changed from 0x%x to 0x%x \n " , g_Telemetry . Level , Level ) ;
g_Telemetry . Level = Level ;
g_TelemetryFrameCount = g_Telemetry . FrameCount ;
g_fTelemetryLevelChanged = true ;
}
}
static void TelemetryPlots ( )
{
if ( g_Telemetry . playbacktick )
{
tmPlotU32 ( TELEMETRY_LEVEL1 , TMPT_INTEGER , 0 , g_Telemetry . playbacktick , " game/PlaybackTick " ) ;
g_Telemetry . playbacktick = 0 ;
}
for ( int i = 0 ; i < g_VProfCurrentProfile . GetNumCounters ( ) ; i + + )
{
if ( g_VProfCurrentProfile . GetCounterGroup ( i ) = = COUNTER_GROUP_TELEMETRY )
{
int val ;
const char * name = g_VProfCurrentProfile . GetCounterNameAndValue ( i , val ) ;
tmPlotI32 ( TELEMETRY_LEVEL1 , TMPT_INTEGER , 0 , val , name ) ;
}
}
g_VProfCurrentProfile . ResetCounters ( COUNTER_GROUP_TELEMETRY ) ;
}
PLATFORM_INTERFACE void TelemetryTick ( )
{
static double s_d0 = Plat_FloatTime ( ) ;
static TmU64 s_t0 = tmFastTime ( ) ;
if ( ! g_Telemetry . Level & & g_Telemetry . DemoTickStart & & ( ( uint32 ) g_Telemetry . playbacktick > g_Telemetry . DemoTickStart ) )
{
TelemetrySetLevel ( 2 ) ;
g_Telemetry . DemoTickStart = 0 ;
}
if ( g_Telemetry . Level & & g_Telemetry . DemoTickEnd & & ( ( uint32 ) g_Telemetry . playbacktick > g_Telemetry . DemoTickEnd ) )
{
TelemetrySetLevel ( 0 ) ;
g_Telemetry . DemoTickEnd = ( uint32 ) - 1 ;
}
// People can NIL out contexts in the TelemetryData structure to control
// the level and what sections to log. We always need to do ticks though,
// so use master context for this.
if ( g_tmContext )
{
// Update any new thread names.
UpdateTelemetryThreadNames ( ) ;
if ( g_Telemetry . Level > 0 )
TelemetryPlots ( ) ;
// Do a Telemetry Tick.
tmTick ( g_tmContext ) ;
// Update flRDTSCToMilliSeconds.
TmU64 s_t1 = tmFastTime ( ) ;
double s_d1 = Plat_FloatTime ( ) ;
g_Telemetry . flRDTSCToMilliSeconds = 1000.0f / ( ( s_t1 - s_t0 ) / ( s_d1 - s_d0 ) ) ;
s_d0 = s_d1 ;
s_t0 = s_t1 ;
// Check if we're only supposed to run X amount of frames.
if ( g_TelemetryFrameCount & & ! tmIsPaused ( g_tmContext ) )
{
g_TelemetryFrameCount - - ;
if ( ! g_TelemetryFrameCount )
TelemetrySetLevel ( 0 ) ;
}
}
if ( g_fTelemetryLevelChanged )
{
g_fTelemetryLevelChanged = false ;
memset ( g_Telemetry . tmContext , 0 , sizeof ( g_Telemetry . tmContext ) ) ;
if ( g_Telemetry . Level = = 0 )
{
// Calling shutdown here invalidates all the telemetry context handles.
// Background threads in the middle of Tm__Zone'd calls may crash...
TelemetryShutdown ( ) ;
}
else
{
if ( ! TelemetryInitialize ( ) )
{
g_Telemetry . Level = 0 ;
}
else
{
tmPause ( g_tmContext , 0 ) ;
uint32 Level = MIN ( g_Telemetry . Level , ARRAYSIZE ( g_Telemetry . tmContext ) ) ;
for ( uint32 i = 0 ; i < Level ; i + + )
{
g_Telemetry . tmContext [ i ] = g_tmContext ;
}
}
}
// TM_SET_TIMELINE_SECTION_NAME( g_tmContext, "Level:0x%x", g_Telemetry.Level );
// To disable various telemetry features, use the tmEnable() function as so:
// TM_ENABLE( g_tmContext, TMO_SUPPORT_PLOT, 0 );
}
}
# endif // RAD_TELEMETRY_ENABLED