//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Real-Time Hierarchical Profiling
//
// $NoKeywords: $
//=============================================================================//

#ifndef VPROF_H
#define VPROF_H

#include "tier0/dbg.h"
#include "tier0/fasttimer.h"
#include "tier0/l2cache.h"
#include "tier0/threadtools.h"
#include "tier0/vprof_telemetry.h"

// VProf is enabled by default in all configurations -except- X360 Retail.
#if !( defined( _GAMECONSOLE ) && defined( _CERT ) )
// #define VPROF_ENABLED
#endif

#if defined(_X360) && defined(VPROF_ENABLED)
#include "tier0/pmc360.h"
#ifndef USE_PIX
#define VPROF_UNDO_PIX
#undef _PIX_H_
#undef PIXBeginNamedEvent
#undef PIXEndNamedEvent
#undef PIXSetMarker
#undef PIXNameThread
#define USE_PIX
#include <pix.h>
#undef USE_PIX
#else
#include <pix.h>
#endif
#endif

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4251)
#endif

// enable this to get detailed nodes beneath budget
// #define VPROF_LEVEL 1

// enable this to use pix (360 only)
// #define VPROF_PIX 1

#if defined(VPROF_PIX)
#pragma comment( lib, "Xapilibi" )
#endif

//-----------------------------------------------------------------------------
//
// Profiling instrumentation macros
//

#define MAXCOUNTERS 256


#ifdef VPROF_ENABLED

#ifndef VPROF_LEVEL
#define VPROF_LEVEL 0
#endif

#define VPROF_VTUNE_GROUP

#define	VPROF( name )						VPROF_(name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, 0)
#define	VPROF_ASSERT_ACCOUNTED( name )		VPROF_(name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, true, 0)
#define	VPROF_( name, detail, group, bAssertAccounted, budgetFlags )		VPROF_##detail(name,group, bAssertAccounted, budgetFlags)

#define VPROF_BUDGET( name, group )					VPROF_BUDGET_FLAGS(name, group, BUDGETFLAG_OTHER)
#define VPROF_BUDGET_FLAGS( name, group, flags )	VPROF_(name, 1, group, false, flags)

#define VPROF_SCOPE_BEGIN( tag )	do { VPROF( tag )
#define VPROF_SCOPE_END()			} while (0)

#define VPROF_ONLY( expression )	expression

#define VPROF_ENTER_SCOPE( name )			g_VProfCurrentProfile.EnterScope( name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, 0 )
#define VPROF_EXIT_SCOPE()					g_VProfCurrentProfile.ExitScope()

#define VPROF_BUDGET_GROUP_ID_UNACCOUNTED 0


// Budgetgroup flags. These are used with VPROF_BUDGET_FLAGS.
// These control which budget panels the groups show up in.
// If a budget group uses VPROF_BUDGET, it gets the default 
// which is BUDGETFLAG_OTHER.
#define BUDGETFLAG_CLIENT	(1<<0)		// Shows up in the client panel.
#define BUDGETFLAG_SERVER	(1<<1)		// Shows up in the server panel.
#define BUDGETFLAG_OTHER	(1<<2)		// Unclassified (the client shows these but the dedicated server doesn't).
#define BUDGETFLAG_HIDDEN	(1<<15)
#define BUDGETFLAG_ALL		0xFFFF


// NOTE: You can use strings instead of these defines. . they are defined here and added
// in vprof.cpp so that they are always in the same order.
#define VPROF_BUDGETGROUP_OTHER_UNACCOUNTED			_T("Unaccounted")
#define VPROF_BUDGETGROUP_WORLD_RENDERING			_T("World Rendering")
#define VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING	_T("Displacement_Rendering")
#define VPROF_BUDGETGROUP_GAME						_T("Game")
#define VPROF_BUDGETGROUP_NPCS						_T("NPCs")
#define VPROF_BUDGETGROUP_SERVER_ANIM				_T("Server Animation")
#define VPROF_BUDGETGROUP_PHYSICS					_T("Physics")
#define VPROF_BUDGETGROUP_STATICPROP_RENDERING		_T("Static_Prop_Rendering")
#define VPROF_BUDGETGROUP_MODEL_RENDERING			_T("Other_Model_Rendering")
#define VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING _T("Fast Path Model Rendering")
#define VPROF_BUDGETGROUP_BRUSHMODEL_RENDERING		_T("Brush_Model_Rendering")
#define VPROF_BUDGETGROUP_SHADOW_RENDERING			_T("Shadow_Rendering")
#define VPROF_BUDGETGROUP_DETAILPROP_RENDERING		_T("Detail_Prop_Rendering")
#define VPROF_BUDGETGROUP_PARTICLE_RENDERING		_T("Particle/Effect_Rendering")
#define VPROF_BUDGETGROUP_ROPES						_T("Ropes")
#define VPROF_BUDGETGROUP_DLIGHT_RENDERING			_T("Dynamic_Light_Rendering")
#define VPROF_BUDGETGROUP_OTHER_NETWORKING			_T("Networking")
#define VPROF_BUDGETGROUP_CLIENT_ANIMATION			_T("Client_Animation")
#define VPROF_BUDGETGROUP_OTHER_SOUND				_T("Sound")
#define VPROF_BUDGETGROUP_OTHER_VGUI				_T("VGUI")
#define VPROF_BUDGETGROUP_OTHER_FILESYSTEM			_T("FileSystem")
#define VPROF_BUDGETGROUP_PREDICTION				_T("Prediction")
#define VPROF_BUDGETGROUP_INTERPOLATION				_T("Interpolation")
#define VPROF_BUDGETGROUP_SWAP_BUFFERS				_T("Swap_Buffers")
#define VPROF_BUDGETGROUP_PLAYER					_T("Player")
#define VPROF_BUDGETGROUP_OCCLUSION					_T("Occlusion")
#define VPROF_BUDGETGROUP_OVERLAYS					_T("Overlays")
#define VPROF_BUDGETGROUP_TOOLS						_T("Tools")
#define VPROF_BUDGETGROUP_LIGHTCACHE				_T("Light_Cache")
#define VPROF_BUDGETGROUP_DISP_HULLTRACES			_T("Displacement_Hull_Traces")
#define VPROF_BUDGETGROUP_TEXTURE_CACHE				_T("Texture_Cache")
#define VPROF_BUDGETGROUP_REPLAY					_T("Replay")
#define VPROF_BUDGETGROUP_PARTICLE_SIMULATION		_T("Particle Simulation")
#define VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING	_T("Flashlight Shadows")
#define VPROF_BUDGETGROUP_CLIENT_SIM				_T("Client Simulation") // think functions, tempents, etc.
#define VPROF_BUDGETGROUP_STEAM						_T("Steam") 
#define VPROF_BUDGETGROUP_CVAR_FIND					_T("Cvar_Find") 
#define VPROF_BUDGETGROUP_CLIENTLEAFSYSTEM			_T("ClientLeafSystem")
#define VPROF_BUDGETGROUP_JOBS_COROUTINES			_T("Jobs/Coroutines")
#define VPROF_BUDGETGROUP_SLEEPING					_T("Sleeping")
#define VPROF_BUDGETGROUP_THREADINGMAIN				_T("ThreadingMain")
#define VPROF_BUDGETGROUP_HTMLSURFACE				_T("HTMLSurface")
#define VPROF_BUDGETGROUP_VGUI						VPROF_BUDGETGROUP_HTMLSURFACE
#define VPROF_BUDGETGROUP_TENFOOT					VPROF_BUDGETGROUP_HTMLSURFACE
#define VPROF_BUDGETGROUP_STEAMUI					VPROF_BUDGETGROUP_HTMLSURFACE
#define VPROF_BUDGETGROUP_ATTRIBUTES				_T("Attributes")
#define VPROF_BUDGETGROUP_FINDATTRIBUTE				_T("FindAttribute")
#define VPROF_BUDGETGROUP_FINDATTRIBUTEUNSAFE		_T("FindAttributeUnsafe")
	
#ifdef _X360
// update flags
#define VPROF_UPDATE_BUDGET				0x01	// send budget data every frame
#define VPROF_UPDATE_TEXTURE_GLOBAL		0x02	// send global texture data every frame
#define VPROF_UPDATE_TEXTURE_PERFRAME	0x04	// send perframe texture data every frame
#endif

//-------------------------------------

//these macros exist to create VProf_<line number> variables. This is important because it avoids /analyze warnings about variable aliasing when VPROF's are nested within each other, and allows
//for multiple VPROF's to exist within the same scope. Three macros must be used to force the __LINE__ to be resolved prior to the token concatenation, but just ignore the _INTERNAL macros and use
//the VPROF_VAR_NAME
#define VPROF_VAR_NAME_INTERNAL_CAT(a, b)	a##b
#define VPROF_VAR_NAME_INTERNAL( a, b )		VPROF_VAR_NAME_INTERNAL_CAT( a, b )
#define VPROF_VAR_NAME( a )					VPROF_VAR_NAME_INTERNAL( a, __LINE__ )

#define	VPROF_0(name,group,assertAccounted,budgetFlags)	tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "(%s)%s", group, name ); CVProfScope VPROF_VAR_NAME( VProf_ )(name, 0, group, assertAccounted, budgetFlags);

#if VPROF_LEVEL > 0 
#define	VPROF_1(name,group,assertAccounted,budgetFlags)	tmZone( TELEMETRY_LEVEL3, TMZF_NONE, "(%s)%s", group, name ); CVProfScope VPROF_VAR_NAME( VProf_ )(name, 1, group, assertAccounted, budgetFlags);
#else
#define	VPROF_1(name,group,assertAccounted,budgetFlags)	((void)0)
#endif

#if VPROF_LEVEL > 1 
#define	VPROF_2(name,group,assertAccounted,budgetFlags)	CVProfScope VPROF_VAR_NAME( VProf_ )(name, 2, group, assertAccounted, budgetFlags);
#else
#define	VPROF_2(name,group,assertAccounted,budgetFlags)	((void)0)
#endif

#if VPROF_LEVEL > 2 
#define	VPROF_3(name,group,assertAccounted,budgetFlags)	CVProfScope VPROF_VAR_NAME( VProf_ )(name, 3, group, assertAccounted, budgetFlags);
#else
#define	VPROF_3(name,group,assertAccounted,budgetFlags)	((void)0)
#endif

#if VPROF_LEVEL > 3 
#define	VPROF_4(name,group,assertAccounted,budgetFlags)	CVProfScope VPROF_VAR_NAME( VProf_ )(name, 4, group, assertAccounted, budgetFlags);
#else
#define	VPROF_4(name,group,assertAccounted,budgetFlags)	((void)0)
#endif

//-------------------------------------

#ifdef _MSC_VER
#define VProfCode( code ) \
	if ( 0 ) \
		; \
	else \
	{ \
	VPROF( __FUNCTION__ ": " #code ); \
		code; \
	}
#else
#define VProfCode( code ) \
	if ( 0 ) \
		; \
	else \
	{ \
		VPROF( #code ); \
		code; \
	} 
#endif


//-------------------------------------

#define VPROF_INCREMENT_COUNTER(name,amount)			do { static CVProfCounter _counter( name ); _counter.Increment( amount ); } while( 0 )
#define VPROF_INCREMENT_GROUP_COUNTER(name,group,amount)			do { static CVProfCounter _counter( name, group ); _counter.Increment( amount ); } while( 0 )

#else

#define	VPROF( name )									((void)0)
#define	VPROF_ASSERT_ACCOUNTED( name )					((void)0)
#define	VPROF_( name, detail, group, bAssertAccounted, budgetFlags )	((void)0)
#define VPROF_BUDGET( name, group )						((void)0)
#define VPROF_BUDGET_FLAGS( name, group, flags )		((void)0)

#define VPROF_SCOPE_BEGIN( tag )	do {
#define VPROF_SCOPE_END()			} while (0)

#define VPROF_ONLY( expression )	((void)0)

#define VPROF_ENTER_SCOPE( name )
#define VPROF_EXIT_SCOPE()

#define VPROF_INCREMENT_COUNTER(name,amount)			((void)0)
#define VPROF_INCREMENT_GROUP_COUNTER(name,group,amount)	((void)0)

#define VPROF_TEST_SPIKE( msec )	((void)0)

#define VProfCode( code ) code

#endif
 
//-----------------------------------------------------------------------------

#ifdef VPROF_ENABLED

//-----------------------------------------------------------------------------
//
// A node in the call graph hierarchy
//

class DBG_CLASS CVProfNode 
{
friend class CVProfRecorder;
friend class CVProfile;

public:
	CVProfNode( const tchar * pszName, int detailLevel, CVProfNode *pParent, const tchar *pBudgetGroupName, int budgetFlags );
	~CVProfNode();
	
	CVProfNode *GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, int budgetFlags );
	CVProfNode *GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName );
	CVProfNode *GetParent();
	CVProfNode *GetSibling();		
	CVProfNode *GetPrevSibling();	
	CVProfNode *GetChild();		
	
	void MarkFrame();
	void ResetPeak();
	
	void Pause();
	void Resume();
	void Reset();

	void EnterScope();
	bool ExitScope();

	const tchar *GetName();

	int GetBudgetGroupID()
	{
		return m_BudgetGroupID;
	}

	// Only used by the record/playback stuff.
	void SetBudgetGroupID( int id )
	{
		m_BudgetGroupID = id;
	}

	int	GetCurCalls();
	double GetCurTime();		
	int GetPrevCalls();
	double GetPrevTime();
	int	GetTotalCalls();
	double GetTotalTime();		
	double GetPeakTime();		

	double GetCurTimeLessChildren();
	double GetPrevTimeLessChildren();
	double GetTotalTimeLessChildren();

	int GetPrevL2CacheMissLessChildren();
	int GetPrevLoadHitStoreLessChildren();

	void ClearPrevTime();

	int GetL2CacheMisses();

	// Not used in the common case...
	void SetCurFrameTime( unsigned long milliseconds );
	
	void SetClientData( int iClientData )	{ m_iClientData = iClientData; }
	int GetClientData() const				{ return m_iClientData; }

#ifdef DBGFLAG_VALIDATE
	void Validate( CValidator &validator, tchar *pchName );		// Validate our internal structures
#endif // DBGFLAG_VALIDATE


// Used by vprof record/playback.
private:

	void SetUniqueNodeID( int id )
	{
		m_iUniqueNodeID = id;
	}

	int GetUniqueNodeID() const
	{
		return m_iUniqueNodeID;
	}

	static int s_iCurrentUniqueNodeID;


private:
	const tchar *m_pszName;
	CFastTimer	m_Timer;

	// L2 Cache data.
	int			m_iPrevL2CacheMiss;
	int			m_iCurL2CacheMiss;
	int			m_iTotalL2CacheMiss;

#ifndef _X360	
	// L2 Cache data.
	CL2Cache	m_L2Cache;
#else // 360:
	
	unsigned int m_iBitFlags; // see enum below for settings
	CPMCData	m_PMCData;
	int			m_iPrevLoadHitStores;
	int			m_iCurLoadHitStores;
	int			m_iTotalLoadHitStores;

	public:
	enum FlagBits
	{
		kRecordL2 = 0x01,
		kCPUTrace = 0x02, ///< cause a PIX trace inside this node.
	};
	// call w/ true to enable L2 and LHS recording; false to turn it off
	inline void EnableL2andLHS(bool enable)
	{
		if (enable)
			m_iBitFlags |= kRecordL2;
		else
			m_iBitFlags &= (~kRecordL2);
	}

	inline bool IsL2andLHSEnabled( void )
	{
		return (m_iBitFlags & kRecordL2) != 0;
	}

	int GetLoadHitStores();

	private:
	
#endif

	unsigned	m_nCurFrameCalls;
	unsigned	m_nPrevFrameCalls;

	int			m_nRecursions;
	
	CCycleCount	m_CurFrameTime;
	
	CCycleCount	m_PrevFrameTime;

	unsigned	m_nTotalCalls;
	CCycleCount	m_TotalTime;

	CCycleCount	m_PeakTime;

	CVProfNode *m_pParent;
	CVProfNode *m_pChild;
	CVProfNode *m_pSibling;

	int m_BudgetGroupID;
	
	int m_iClientData;
	int m_iUniqueNodeID;
};

//-----------------------------------------------------------------------------
//
// Coordinator and root node of the profile hierarchy tree
//

enum VProfReportType_t
{
	VPRT_SUMMARY									= ( 1 << 0 ),
	VPRT_HIERARCHY									= ( 1 << 1 ),
	VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY	= ( 1 << 2 ),
	VPRT_LIST_BY_TIME								= ( 1 << 3 ),
	VPRT_LIST_BY_TIME_LESS_CHILDREN					= ( 1 << 4 ),
	VPRT_LIST_BY_AVG_TIME							= ( 1 << 5 ),	
	VPRT_LIST_BY_AVG_TIME_LESS_CHILDREN				= ( 1 << 6 ),
	VPRT_LIST_BY_PEAK_TIME							= ( 1 << 7 ),
	VPRT_LIST_BY_PEAK_OVER_AVERAGE					= ( 1 << 8 ),
	VPRT_LIST_TOP_ITEMS_ONLY						= ( 1 << 9 ),

	VPRT_FULL = (0xffffffff & ~(VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY|VPRT_LIST_TOP_ITEMS_ONLY)),
};

enum CounterGroup_t
{
	COUNTER_GROUP_DEFAULT=0,
	COUNTER_GROUP_NO_RESET,				// The engine doesn't reset these counters. Usually, they are used 
										// like global variables that can be accessed across modules.
	COUNTER_GROUP_TEXTURE_GLOBAL,		// Global texture usage counters (totals for what is currently in memory).
	COUNTER_GROUP_TEXTURE_PER_FRAME,	// Per-frame texture usage counters.

	COUNTER_GROUP_TELEMETRY,
}; 

class DBG_CLASS CVProfile 
{
public:
	CVProfile();
	~CVProfile();

	void Term();
	
	//
	// Runtime operations
	//
	
	void Start();
	void Stop();

	void SetTargetThreadId( unsigned id ) { m_TargetThreadId = id; }
	unsigned GetTargetThreadId() { return m_TargetThreadId; }
	bool InTargetThread() { return ( m_TargetThreadId == ThreadGetCurrentId() ); }

#ifdef _X360
	enum VXConsoleReportMode_t
	{
		VXCONSOLE_REPORT_TIME = 0,
		VXCONSOLE_REPORT_L2CACHE_MISSES,
		VXCONSOLE_REPORT_LOAD_HIT_STORE,
		VXCONSOLE_REPORT_COUNT,
	};

	void VXProfileStart();
	void VXProfileUpdate();
	void VXEnableUpdateMode( int event, bool bEnable );
	void VXSendNodes( void );
	
	void PMCDisableAllNodes(CVProfNode *pStartNode = NULL);  ///< turn off l2 and lhs recording for everywhere
	bool PMCEnableL2Upon(const tchar *pszNodeName, bool bRecursive = false); ///< enable l2 and lhs recording for one given node
	bool PMCDisableL2Upon(const tchar *pszNodeName, bool bRecursive = false); ///< enable l2 and lhs recording for one given node

	void DumpEnabledPMCNodes( void );

	void VXConsoleReportMode( VXConsoleReportMode_t mode );
	void VXConsoleReportScale( VXConsoleReportMode_t mode, float flScale );

	// the CPU trace mode is actually a small state machine; it can be off, primed for
	// single capture, primed for everything-in-a-frame capture, or currently in everything-in-a-frame
	// capture.
	enum CPUTraceState
	{
		kDisabled,
		kFirstHitNode,						// record from the first time we hit the node until that node ends
		kAllNodesInFrame_WaitingForMark,	// we're going to record all the times a node is hit in a frame, but are waiting for the frame to start
		kAllNodesInFrame_Recording,			// we're recording all hits on a node this frame.

		// Same as above, but going to record for > 1 frame
		kAllNodesInFrame_WaitingForMarkMultiFrame,	// we're going to record all the times a node is hit in a frame, but are waiting for the frame to start
		kAllNodesInFrame_RecordingMultiFrame, 
	};

	// Global switch to turn CPU tracing on or off at all. The idea is you set up a node first,
	// then trigger tracing by throwing this to true. It'll reset back to false after the trace 
	// happens.
	inline CPUTraceState GetCPUTraceMode();
	inline void SetCPUTraceEnabled( CPUTraceState enabled, bool bTraceCompleteEvent = false, int nNumFrames = -1 );
	inline void IncrementMultiTraceIndex(); // tick up the counter that gets appended to the multi-per-frame traces
	inline unsigned int GetMultiTraceIndex(); // return the counter
	void CPUTraceDisableAllNodes( CVProfNode *pStartNode = NULL ); // disable the cpu trace flag wherever it may be
	CVProfNode *CPUTraceEnableForNode( const tchar *pszNodeName ); // enable cpu trace on this node only, disabling it wherever else it may be on.
	CVProfNode *CPUTraceGetEnabledNode( CVProfNode *pStartNode = NULL ); // return the node enabled for CPU tracing, or NULL.
	const char *GetCPUTraceFilename(); // get the filename the trace should write into.
	const char *SetCPUTraceFilename( const char *filename ); // set the filename the trace should write into. (don't specify the extension; I'll do that.)
	inline bool TraceCompleteEvent( void );

#ifdef _X360
	void LatchMultiFrame( int64 cycles );
	void SpewWorstMultiFrame();
#endif

#endif

	void EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted );
	void EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags );
	void ExitScope();

	void MarkFrame();
	void ResetPeaks();
	
	void Pause();
	void Resume();
	void Reset();
	
	bool IsEnabled() const;
	int GetDetailLevel() const;

	bool AtRoot() const;

	//
	// Queries
	//

#ifdef VPROF_VTUNE_GROUP
#	define MAX_GROUP_STACK_DEPTH 1024

	void EnableVTuneGroup( const tchar *pGroupName )
	{
		m_nVTuneGroupID = BudgetGroupNameToBudgetGroupID( pGroupName );
		m_bVTuneGroupEnabled = true;
	}
	void DisableVTuneGroup( void )
	{
		m_bVTuneGroupEnabled = false;
	}
	
	inline void PushGroup( int nGroupID );
	inline void PopGroup( void );
#endif
	
	int NumFramesSampled()	{ return m_nFrames; }
	double GetPeakFrameTime();
	double GetTotalTimeSampled();
	double GetTimeLastFrame();
	
	CVProfNode *GetRoot();
	CVProfNode *FindNode( CVProfNode *pStartNode, const tchar *pszNode );
	CVProfNode *GetCurrentNode();

	typedef void ( __cdecl *StreamOut_t )( const char* pszFormat, ... );
	// Set the output function used for all vprof reports. Call this with NULL
	// to set it to the default output function.
	void SetOutputStream( StreamOut_t outputStream );
	void OutputReport( int type = VPRT_FULL, const tchar *pszStartNode = NULL, int budgetGroupID = -1 );

	const tchar *GetBudgetGroupName( int budgetGroupID );
	int GetBudgetGroupFlags( int budgetGroupID ) const;	// Returns a combination of BUDGETFLAG_ defines.
	int GetNumBudgetGroups( void );
	void GetBudgetGroupColor( int budgetGroupID, int &r, int &g, int &b, int &a );
	int BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName );
	int BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName, int budgetFlagsToORIn );
	void RegisterNumBudgetGroupsChangedCallBack( void (*pCallBack)(void) );

	int BudgetGroupNameToBudgetGroupIDNoCreate( const tchar *pBudgetGroupName ) { return FindBudgetGroupName( pBudgetGroupName ); }

	void HideBudgetGroup( int budgetGroupID, bool bHide = true );
	void HideBudgetGroup( const char *pszName, bool bHide = true ) { HideBudgetGroup( BudgetGroupNameToBudgetGroupID( pszName), bHide ); }

	int *FindOrCreateCounter( const tchar *pName, CounterGroup_t eCounterGroup=COUNTER_GROUP_DEFAULT  );
	void ResetCounters( CounterGroup_t eCounterGroup );
	
	int GetNumCounters( void ) const;
	
	const tchar *GetCounterName( int index ) const;
	int GetCounterValue( int index ) const;
	const tchar *GetCounterNameAndValue( int index, int &val ) const;
	CounterGroup_t GetCounterGroup( int index ) const;

	// Performance monitoring events.
	void PMEInitialized( bool bInit )		{ m_bPMEInit = bInit; }
	void PMEEnable( bool bEnable )			{ m_bPMEEnabled = bEnable; }

#ifndef _X360
	bool UsePME( void )						{ return ( m_bPMEInit && m_bPMEEnabled ); }
#else
	bool UsePME( void )						{ return ( CPMCData::IsInitialized() && m_bPMEEnabled ); }
#endif

#ifdef DBGFLAG_VALIDATE
	void Validate( CValidator &validator, tchar *pchName );		// Validate our internal structures
#endif // DBGFLAG_VALIDATE

protected:

	void FreeNodes_R( CVProfNode *pNode );

#ifdef VPROF_VTUNE_GROUP
	bool VTuneGroupEnabled()
	{ 
		return m_bVTuneGroupEnabled; 
	}
	int VTuneGroupID() 
	{ 
		return m_nVTuneGroupID; 
	}
#endif

	void SumTimes( const tchar *pszStartNode, int budgetGroupID );
	void SumTimes( CVProfNode *pNode, int budgetGroupID );
	void DumpNodes( CVProfNode *pNode, int indent, bool bAverageAndCountOnly );
	int FindBudgetGroupName( const tchar *pBudgetGroupName );
	int AddBudgetGroupName( const tchar *pBudgetGroupName, int budgetFlags );

#ifdef VPROF_VTUNE_GROUP
	bool		m_bVTuneGroupEnabled;
	int			m_nVTuneGroupID;
	int			m_GroupIDStack[MAX_GROUP_STACK_DEPTH];
	int			m_GroupIDStackDepth;
#endif
	CVProfNode	m_Root;
	CVProfNode *m_pCurNode;

	int			m_nFrames;
	int 		m_enabled;
	int			m_pausedEnabledDepth;
	bool		m_fAtRoot; // tracked for efficiency of the "not profiling" case
	int			m_ProfileDetailLevel;

	class CBudgetGroup
	{
	public:
		tchar *m_pName;
		int m_BudgetFlags;
	};
	
	CBudgetGroup	*m_pBudgetGroups;
	int			m_nBudgetGroupNamesAllocated;
	int			m_nBudgetGroupNames;
	void		(*m_pNumBudgetGroupsChangedCallBack)(void);

	// Performance monitoring events.
	bool		m_bPMEInit;
	bool		m_bPMEEnabled;

	int m_Counters[MAXCOUNTERS];
	char m_CounterGroups[MAXCOUNTERS]; // (These are CounterGroup_t's).
	tchar *m_CounterNames[MAXCOUNTERS];
	int m_NumCounters;

#ifdef _X360
	int						m_UpdateMode;
	CPUTraceState			m_iCPUTraceEnabled;
	int						m_nFramesRemaining;
	int						m_nFrameCount;
	int64					m_WorstCycles;
	char					m_WorstTraceFilename[128];
	char					m_CPUTraceFilename[128];
	unsigned int			m_iSuccessiveTraceIndex;
	VXConsoleReportMode_t	m_ReportMode;
	float					m_pReportScale[VXCONSOLE_REPORT_COUNT];
	bool					m_bTraceCompleteEvent;
#endif

	unsigned m_TargetThreadId;

	StreamOut_t				m_pOutputStream;
};

//-------------------------------------

DBG_INTERFACE CVProfile g_VProfCurrentProfile;


//-----------------------------------------------------------------------------

DBG_INTERFACE bool g_VProfSignalSpike;

class CVProfSpikeDetector
{
public:
	CVProfSpikeDetector( float spike ) :
		m_timeLast( GetTimeLast() )
	{
		m_spike = spike;
		m_Timer.Start();
	}

	~CVProfSpikeDetector()
	{
		m_Timer.End();
		if ( Plat_FloatTime() - m_timeLast > 2.0 )
		{
			m_timeLast = Plat_FloatTime();
			if ( m_Timer.GetDuration().GetMillisecondsF() > m_spike )
			{
				g_VProfSignalSpike = true;
			}
		}
	}

private:
	static float &GetTimeLast() { static float timeLast = 0; return timeLast; }
	CFastTimer	m_Timer;
	float m_spike;
	float &m_timeLast;
};


// Macro to signal a local spike. Meant as temporary instrumentation, do not leave in code
#define VPROF_TEST_SPIKE( msec ) CVProfSpikeDetector UNIQUE_ID( msec )

//-----------------------------------------------------------------------------

#ifdef VPROF_VTUNE_GROUP
inline void CVProfile::PushGroup( int nGroupID )
{
	// There is always at least one item on the stack since we force 
	// the first element to be VPROF_BUDGETGROUP_OTHER_UNACCOUNTED.
	Assert( m_GroupIDStackDepth > 0 );
	Assert( m_GroupIDStackDepth < MAX_GROUP_STACK_DEPTH );
	m_GroupIDStack[m_GroupIDStackDepth] = nGroupID;
	m_GroupIDStackDepth++;
	if( m_GroupIDStack[m_GroupIDStackDepth-2] != nGroupID && 
		VTuneGroupEnabled() &&
		nGroupID == VTuneGroupID() )
	{
		vtune( true );
	}
}
#endif // VPROF_VTUNE_GROUP

#ifdef VPROF_VTUNE_GROUP
inline void CVProfile::PopGroup( void )
{
	m_GroupIDStackDepth--;
	// There is always at least one item on the stack since we force 
	// the first element to be VPROF_BUDGETGROUP_OTHER_UNACCOUNTED.
	Assert( m_GroupIDStackDepth > 0 );
	if(	m_GroupIDStack[m_GroupIDStackDepth] != m_GroupIDStack[m_GroupIDStackDepth+1] && 
		VTuneGroupEnabled() &&
		m_GroupIDStack[m_GroupIDStackDepth+1] == VTuneGroupID() )
	{
		vtune( false );
	}
}
#endif // VPROF_VTUNE_GROUP

//-----------------------------------------------------------------------------

class CVProfScope
{
public:
	CVProfScope( const tchar * pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags );
	~CVProfScope();

private:
	bool m_bEnabled;
};

//-----------------------------------------------------------------------------
//
// CVProfNode, inline methods
//

inline CVProfNode::CVProfNode( const tchar * pszName, int detailLevel, CVProfNode *pParent, const tchar *pBudgetGroupName, int budgetFlags )
 :	m_pszName( pszName ),
	m_nCurFrameCalls( 0 ),
	m_nPrevFrameCalls( 0 ),
	m_nRecursions( 0 ),
	m_pParent( pParent ),
	m_pChild( NULL ),
	m_pSibling( NULL ),
	m_iClientData( -1 )
#ifdef _X360
	, m_iBitFlags( 0 )
#endif
{
	m_iUniqueNodeID = s_iCurrentUniqueNodeID++;

	if ( m_iUniqueNodeID > 0 )
	{
		m_BudgetGroupID = g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( pBudgetGroupName, budgetFlags );
	}
	else
	{
		m_BudgetGroupID = 0; // "m_Root" can't call BudgetGroupNameToBudgetGroupID because g_VProfCurrentProfile not yet initialized
	}

	Reset();

	if( m_pParent && ( m_BudgetGroupID == VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) )
	{
		m_BudgetGroupID = m_pParent->GetBudgetGroupID();
	}
}


//-------------------------------------

inline CVProfNode *CVProfNode::GetParent()		
{ 
	Assert( m_pParent );
	return m_pParent; 
}

//-------------------------------------

inline CVProfNode *CVProfNode::GetSibling()		
{ 
	return m_pSibling; 
}

//-------------------------------------
// Hacky way to the previous sibling, only used from vprof panel at the moment,
// so it didn't seem like it was worth the memory waste to add the reverse
// link per node.

inline CVProfNode *CVProfNode::GetPrevSibling()		
{ 
	CVProfNode* p = GetParent();

	if(!p) 
		return NULL;

	CVProfNode* s;
	for( s = p->GetChild(); 
	     s && ( s->GetSibling() != this ); 
		 s = s->GetSibling() )
		;

	return s;	
}

//-------------------------------------

inline CVProfNode *CVProfNode::GetChild()			
{ 
	return m_pChild; 
}

//-------------------------------------

inline const tchar *CVProfNode::GetName()				
{ 
	Assert( m_pszName );
	return m_pszName; 
}

//-------------------------------------

inline int	CVProfNode::GetTotalCalls()		
{ 
	return m_nTotalCalls; 
}

//-------------------------------------

inline double CVProfNode::GetTotalTime()		
{ 
	return m_TotalTime.GetMillisecondsF();
}

//-------------------------------------

inline int	CVProfNode::GetCurCalls()		
{ 
	return m_nCurFrameCalls; 
}

//-------------------------------------

inline double CVProfNode::GetCurTime()		
{ 
	return m_CurFrameTime.GetMillisecondsF();
}

//-------------------------------------

inline int CVProfNode::GetPrevCalls()
{
	return m_nPrevFrameCalls;
}

//-------------------------------------

inline double CVProfNode::GetPrevTime()		
{ 
	return m_PrevFrameTime.GetMillisecondsF();
}

//-------------------------------------

inline double CVProfNode::GetPeakTime()		
{ 
	return m_PeakTime.GetMillisecondsF();
}

//-------------------------------------

inline double CVProfNode::GetTotalTimeLessChildren()
{
	double result = GetTotalTime();
	CVProfNode *pChild = GetChild();
	while ( pChild )
	{
		result -= pChild->GetTotalTime();
		pChild = pChild->GetSibling();
	}
	return result;
}

//-------------------------------------

inline double CVProfNode::GetCurTimeLessChildren()
{
	double result = GetCurTime();
	CVProfNode *pChild = GetChild();
	while ( pChild )
	{
		result -= pChild->GetCurTime();
		pChild = pChild->GetSibling();
	}
	return result;
}

inline double CVProfNode::GetPrevTimeLessChildren()
{
	double result = GetPrevTime();
	CVProfNode *pChild = GetChild();
	while ( pChild )
	{
		result -= pChild->GetPrevTime();
		pChild = pChild->GetSibling();
	}
	return result;
}

//-----------------------------------------------------------------------------
inline int CVProfNode::GetPrevL2CacheMissLessChildren()
{
	int result = m_iPrevL2CacheMiss;
	CVProfNode *pChild = GetChild();
	while ( pChild )
	{
		result -= pChild->m_iPrevL2CacheMiss;
		pChild = pChild->GetSibling();
	}
	return result;
}

//-----------------------------------------------------------------------------
inline int CVProfNode::GetPrevLoadHitStoreLessChildren()
{
#ifndef _X360
	return 0;
#else
	int result = m_iPrevLoadHitStores;
	CVProfNode *pChild = GetChild();
	while ( pChild )
	{
		result -= pChild->m_iPrevLoadHitStores;
		pChild = pChild->GetSibling();
	}
	return result;
#endif
}


//-----------------------------------------------------------------------------
inline void CVProfNode::ClearPrevTime()
{
	m_PrevFrameTime.Init();
}

//-----------------------------------------------------------------------------
inline int CVProfNode::GetL2CacheMisses( void )
{ 
#ifndef _X360
	return m_L2Cache.GetL2CacheMisses(); 
#else
	return m_iTotalL2CacheMiss;
#endif
}

#ifdef _X360
inline int CVProfNode::GetLoadHitStores( void )
{
	return m_iTotalLoadHitStores;
}
#endif

//-----------------------------------------------------------------------------
//
// CVProfile, inline methods
//

//-------------------------------------

inline bool CVProfile::IsEnabled() const	
{ 
	return ( m_enabled != 0 ); 
}

//-------------------------------------

inline int CVProfile::GetDetailLevel() const	
{ 
	return m_ProfileDetailLevel; 
}

	
//-------------------------------------

inline bool CVProfile::AtRoot() const
{
	return m_fAtRoot;
}
	
//-------------------------------------

inline void CVProfile::Start()	
{ 
	if ( ++m_enabled == 1 )
	{
		m_Root.EnterScope();
#ifdef _X360
		VXProfileStart();
		CPMCData::InitializeOnceProgramWide();
#endif
	}
}

//-------------------------------------

inline void CVProfile::Stop()		
{ 
	if ( --m_enabled == 0 )
		m_Root.ExitScope();
}

//-------------------------------------

inline void CVProfile::EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags )
{
	if ( ( m_enabled != 0 || !m_fAtRoot ) && InTargetThread() ) // if became disabled, need to unwind back to root before stopping
	{
		// Only account for vprof stuff on the primary thread.
		//if( !Plat_IsPrimaryThread() )
		//	return;

		if ( pszName != m_pCurNode->GetName() ) 
		{
			m_pCurNode = m_pCurNode->GetSubNode( pszName, detailLevel, pBudgetGroupName, budgetFlags );
		}
		m_pBudgetGroups[m_pCurNode->GetBudgetGroupID()].m_BudgetFlags |= budgetFlags;

#if defined( _DEBUG ) && !defined( _X360 )
		// 360 doesn't want this to allow tier0 debug/release .def files to match
		if ( bAssertAccounted )
		{
			// FIXME
			AssertOnce( m_pCurNode->GetBudgetGroupID() != 0 );
		}
#endif
		m_pCurNode->EnterScope();
		m_fAtRoot = false;
	}
#if defined(_X360) && defined(VPROF_PIX)
	if ( m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED )
		PIXBeginNamedEvent( 0, pszName );
#endif
}

inline void CVProfile::EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted )
{
	EnterScope( pszName, detailLevel, pBudgetGroupName, bAssertAccounted, BUDGETFLAG_OTHER );
}

//-------------------------------------

inline void CVProfile::ExitScope()
{
#if defined(_X360) && defined(VPROF_PIX)
#ifdef PIXBeginNamedEvent
#error
#endif
	if ( m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED )
		PIXEndNamedEvent();
#endif
	if ( ( !m_fAtRoot || m_enabled != 0 ) && InTargetThread() )
	{
		// Only account for vprof stuff on the primary thread.
		//if( !Plat_IsPrimaryThread() )
		//	return;

		// ExitScope will indicate whether we should back up to our parent (we may
		// be profiling a recursive function)
		if (m_pCurNode->ExitScope()) 
		{
			m_pCurNode = m_pCurNode->GetParent();
		}
		m_fAtRoot = ( m_pCurNode == &m_Root );
	}
}

//-------------------------------------

inline void CVProfile::Pause()
{
	m_pausedEnabledDepth = m_enabled;
	m_enabled = 0;
	if ( !AtRoot() )
		m_Root.Pause(); 
}

//-------------------------------------

inline void CVProfile::Resume()
{
	m_enabled = m_pausedEnabledDepth;
	if ( !AtRoot() )
		m_Root.Resume(); 
}

//-------------------------------------

inline void CVProfile::Reset()
{
	m_Root.Reset(); 
	m_nFrames = 0;
}

//-------------------------------------

inline void CVProfile::ResetPeaks()
{
	m_Root.ResetPeak(); 
}

//-------------------------------------

inline void CVProfile::MarkFrame()
{
	if ( m_enabled )
	{
		++m_nFrames;
		m_Root.ExitScope();
		m_Root.MarkFrame(); 
		m_Root.EnterScope();

#ifdef _X360
		// update the CPU trace state machine if enabled
		switch ( GetCPUTraceMode() )
		{
		case kAllNodesInFrame_WaitingForMark:
			// mark! Start recording a zillion traces.
			m_iCPUTraceEnabled = kAllNodesInFrame_Recording;
			break;
		case kAllNodesInFrame_WaitingForMarkMultiFrame:
			m_iCPUTraceEnabled = kAllNodesInFrame_RecordingMultiFrame;
			break;
		case kAllNodesInFrame_Recording:
			// end of frame. stop recording if no more frames needed
			m_iCPUTraceEnabled = kDisabled;
			Msg("Frame ended. Recording no more CPU traces\n");

			break;
		case kAllNodesInFrame_RecordingMultiFrame:
			// end of frame. stop recording if no more frames needed
			if ( --m_nFramesRemaining == 0 )
			{
				m_iCPUTraceEnabled = kDisabled;
				Msg("Frames ended. Recording no more CPU traces\n");

				SpewWorstMultiFrame();
			}

			++m_nFrameCount;

			break;
		default:
			// no default
			break;
		}
#endif
	}
}

//-------------------------------------

inline double CVProfile::GetTotalTimeSampled()
{
	return m_Root.GetTotalTime();
}

//-------------------------------------

inline double CVProfile::GetPeakFrameTime()
{
	return m_Root.GetPeakTime();
}

//-------------------------------------

inline double CVProfile::GetTimeLastFrame()
{
	return m_Root.GetCurTime();
}
	
//-------------------------------------

inline CVProfNode *CVProfile::GetRoot()
{
	return &m_Root;
}

//-------------------------------------

inline CVProfNode *CVProfile::GetCurrentNode()
{
	return m_pCurNode;
}


inline const tchar *CVProfile::GetBudgetGroupName( int budgetGroupID )
{
	Assert( budgetGroupID >= 0 && budgetGroupID < m_nBudgetGroupNames );
	return m_pBudgetGroups[budgetGroupID].m_pName;
}

inline int CVProfile::GetBudgetGroupFlags( int budgetGroupID ) const
{
	Assert( budgetGroupID >= 0 && budgetGroupID < m_nBudgetGroupNames );
	return m_pBudgetGroups[budgetGroupID].m_BudgetFlags;
}

#ifdef _X360

inline CVProfile::CPUTraceState CVProfile::GetCPUTraceMode()
{
	return m_iCPUTraceEnabled;
}

inline void CVProfile::SetCPUTraceEnabled( CPUTraceState enabled, bool bTraceCompleteEvent /*=true*/, int nNumFrames /*= -1*/ )
{
	m_iCPUTraceEnabled = enabled;
	m_bTraceCompleteEvent = bTraceCompleteEvent;
	if ( nNumFrames != -1 )
	{
		m_nFramesRemaining = nNumFrames;
		m_nFrameCount = 0;
		m_WorstCycles = 0;
		m_WorstTraceFilename[ 0 ] = 0;
	}
}

inline void CVProfile::IncrementMultiTraceIndex()
{
	++m_iSuccessiveTraceIndex;
}

inline unsigned int CVProfile::GetMultiTraceIndex()
{
	return m_iSuccessiveTraceIndex;
}

#endif


//-----------------------------------------------------------------------------

inline CVProfScope::CVProfScope( const tchar * pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags )
	: m_bEnabled( g_VProfCurrentProfile.IsEnabled() )
{ 
	if ( m_bEnabled )
	{
		g_VProfCurrentProfile.EnterScope( pszName, detailLevel, pBudgetGroupName, bAssertAccounted, budgetFlags ); 
	}
}

//-------------------------------------

inline CVProfScope::~CVProfScope()					
{ 
	if ( m_bEnabled )
	{
		g_VProfCurrentProfile.ExitScope(); 
	}
}

class CVProfCounter
{
public:
	CVProfCounter( const tchar *pName, CounterGroup_t group=COUNTER_GROUP_DEFAULT )
	{
		m_pCounter = g_VProfCurrentProfile.FindOrCreateCounter( pName, group );
		Assert( m_pCounter );
	}
	~CVProfCounter()
	{
	}
	void Increment( int val ) 
	{ 
		Assert( m_pCounter );
		*m_pCounter += val; 
	}
private:
	int *m_pCounter;
};

#endif

#ifdef _X360

#include "xbox/xbox_console.h"
#include "tracerecording.h"
#include  "tier1/fmtstr.h"
#pragma comment( lib, "tracerecording.lib" )
#pragma comment( lib, "xbdm.lib" )

class CPIXRecorder
{
public:
	CPIXRecorder() : m_bActive( false ) {}
	~CPIXRecorder() { Stop(); }

	void Start( const char *pszFilename = "capture" )
	{
		if ( !m_bActive )
		{
			if ( !XTraceStartRecording( CFmtStr( "e:\\%s.pix2", pszFilename ) ) )
			{
				Msg( "XTraceStartRecording failed, error code %d\n", GetLastError() );
			}
			else
			{
				m_bActive = true;
			}
		}
	}

	void Stop()
	{
		if ( m_bActive )
		{
			m_bActive = false;
			if ( XTraceStopRecording() )
			{
				Msg( "CPU trace finished.\n" );
				// signal VXConsole that trace is completed
				XBX_rTraceComplete();
			}
		}
	}

private:
	bool m_bActive;
};

#define VPROF_BEGIN_PIX_BLOCK( convar ) \
	{ \
	bool bRunPix = 0; \
	static CFastTimer PIXTimer; \
	extern ConVar convar; \
	ConVar &PIXConvar = convar; \
	CPIXRecorder PIXRecorder; \
		{ \
		PIXLabel: \
			if ( bRunPix ) \
			{ \
				PIXRecorder.Start(); \
			} \
			else \
			{ \
				if ( PIXConvar.GetBool() ) \
				{ \
					PIXTimer.Start(); \
				} \
			} \
				{


#define VPROF_END_PIX_BLOCK() \
				} \
			\
			if ( !bRunPix ) \
			{ \
				if ( PIXConvar.GetBool() ) \
				{ \
					PIXTimer.End(); \
					if ( PIXTimer.GetDuration().GetMillisecondsF() > PIXConvar.GetFloat() ) \
					{ \
						PIXConvar.SetValue( 0 ); \
						bRunPix = true; \
						goto PIXLabel; \
					} \
				} \
			} \
			else \
			{ \
				PIXRecorder.Stop(); \
			} \
		} \
	}
#else
#define VPROF_BEGIN_PIX_BLOCK( PIXConvar ) {
#define VPROF_END_PIX_BLOCK() }
#endif


#ifdef VPROF_UNDO_PIX
#undef USE_PIX
#undef _PIX_H_
#undef PIXBeginNamedEvent
#undef PIXEndNamedEvent
#undef PIXSetMarker
#undef PIXNameThread
#include <pix.h>
#endif

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif

//=============================================================================