#ifndef VPROF_H
#define VPROF_H

#if !defined(__SPU__)

#include "dbg.h"
#include "fasttimer.h"
#include "l2cache.h"
#include "threadtools.h"
#include "vprof_sn.h"
#include "vprof_telemetry.h"

#if !( defined( _GAMECONSOLE ) && defined( _CERT ) ) && !defined( _PS3 )
#define VPROF_ENABLED
#endif

#if defined(_X360) && defined(VPROF_ENABLED)

#ifdef PROFILE
#define VPROF_PIX 1
#endif

#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

#if defined( _X360 ) && defined( PROFILE )
#define VPROF_PIX 1
#endif

#if defined(_X360) || defined(_PS3)
#define VPROF_VXCONSOLE_EXISTS 1
#endif


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

#define MAXCOUNTERS 256


#ifdef VPROF_ENABLED

#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, 0, 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


#define BUDGETFLAG_CLIENT	(1<<0)		      
#define BUDGETFLAG_SERVER	(1<<1)		      
#define BUDGETFLAG_OTHER	(1<<2)		          
#define BUDGETFLAG_HIDDEN	(1<<15)
#define BUDGETFLAG_ALL		0xFFFF


#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_BRUSH_FAST_PATH_RENDERING _T("Fast Path Brush 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")     
#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_ENCRYPTION				_T("Encryption")


#define VPROF_BUDGETGROUP_CHROMEHTML				_T("Chromehtml")
#define VPROF_BUDGETGROUP_VGUI						VPROF_BUDGETGROUP_CHROMEHTML
#define VPROF_BUDGETGROUP_TENFOOT					VPROF_BUDGETGROUP_CHROMEHTML
#define VPROF_BUDGETGROUP_STEAMUI					VPROF_BUDGETGROUP_CHROMEHTML

#ifdef VPROF_VXCONSOLE_EXISTS
#define VPROF_UPDATE_BUDGET				0x01	     
#define VPROF_UPDATE_TEXTURE_GLOBAL		0x02	      
#define VPROF_UPDATE_TEXTURE_PERFRAME	0x04	      
#endif

#ifndef VPROF_LEVEL
#endif

#if !defined( VPROF_SN_LEVEL ) && !defined( _CERT )
#endif

#define VPROF_SCOPE_VARIABLE_NAME( prefix, line ) prefix##line
#define VPROF_SCOPE_VARIABLE_DECL( name, level, group, assertAccounted, budgetFlags, line ) CVProfScope VPROF_SCOPE_VARIABLE_NAME( VProf_,line )(name, level, group, assertAccounted, budgetFlags)

#define	VPROF_0(name,group,assertAccounted,budgetFlags)	TM_ZONE( TELEMETRY_LEVEL2, TMZF_NONE, "(%s)%s", group, name ); VPROF_SCOPE_VARIABLE_DECL(name, 0, group, assertAccounted, budgetFlags, __LINE__ );

#if VPROF_LEVEL > 0 
#  define	VPROF_1(name,group,assertAccounted,budgetFlags)	TM_ZONE( TELEMETRY_LEVEL3, TMZF_NONE, "(%s)%s", group, name ); VPROF_SCOPE_VARIABLE_DECL(name, 1, group, assertAccounted, budgetFlags, __LINE__ );
#else
#  if VPROF_SN_LEVEL > 0 && defined( _PS3 )
#	 define	VPROF_1(name,group,assertAccounted,budgetFlags)	CVProfSnMarkerScope VProfSn_( name )
#  else
#    define	VPROF_1(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif
#endif

#if VPROF_LEVEL > 1 
#define	VPROF_2(name,group,assertAccounted,budgetFlags)	TM_ZONE( TELEMETRY_LEVEL4, TMZF_NONE, "(%s)%s", group, name ); VPROF_SCOPE_VARIABLE_DECL(name, 2, group, assertAccounted, budgetFlags, __LINE__);
#else
#  if VPROF_SN_LEVEL > 1 && defined( _PS3 )
#	 define	VPROF_2(name,group,assertAccounted,budgetFlags)	CVProfSnMarkerScope VProfSn_( name )
#  else
#    define	VPROF_2(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif
#endif

#if VPROF_LEVEL > 2 
#define	VPROF_3(name,group,assertAccounted,budgetFlags)	TM_ZONE( TELEMETRY_LEVEL5, TMZF_NONE, "(%s)%s", group, name ); VPROF_SCOPE_VARIABLE_DECL(name, 3, group, assertAccounted, budgetFlags, __LINE__);
#else
#  if VPROF_SN_LEVEL > 2 && defined( _PS3 )
#	 define	VPROF_3(name,group,assertAccounted,budgetFlags)	CVProfSnMarkerScope VProfSn_( name )
#  else
#    define	VPROF_3(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif
#endif

#if VPROF_LEVEL > 3 
#define	VPROF_4(name,group,assertAccounted,budgetFlags)	TM_ZONE( TELEMETRY_LEVEL6, TMZF_NONE, "(%s)%s", group, name ); VPROF_SCOPE_VARIABLE_DECL(name, 4, group, assertAccounted, budgetFlags, __LINE__);
#else
#  if VPROF_SN_LEVEL > 3 && defined( _PS3 )
#	 define	VPROF_4(name,group,assertAccounted,budgetFlags)	CVProfSnMarkerScope VProfSn_( name )
#  else
#    define	VPROF_4(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif
#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 )
#define VPROF_SET_COUNTER(name,amount)			do { static CVProfCounter _counter( name ); _counter.Set( amount ); } while( 0 )
#define VPROF_SET_GROUP_COUNTER(name,group,amount)			do { static CVProfCounter _counter( name, group ); _counter.Set( amount ); } while( 0 )

#else

#  if defined( VPROF_SN_LEVEL ) && ( VPROF_SN_LEVEL >= 0 )
#    define	VPROF( name )									CVProfSnMarkerScope VProfSn_( name )
#    define	VPROF_ASSERT_ACCOUNTED( name )					VPROF( name )
#    define	VPROF_( name, detail, group, bAssertAccounted, budgetFlags )	VPROF_##detail( name, group, bAssertAccounted, budgetFlags )
#	 define	VPROF_0(name,group,assertAccounted,budgetFlags)	VPROF( name )
#    define VPROF_BUDGET( name, group )						VPROF( name )
#    define VPROF_BUDGET_FLAGS( name, group, flags )		VPROF( name )

#    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_pfnPushMarker( name )
#    define VPROF_EXIT_SCOPE()   g_pfnPopMarker()
#  else
#    define	VPROF( name )									((void)0)
#    define	VPROF_ASSERT_ACCOUNTED( name )					((void)0)
#    define	VPROF_( name, detail, group, bAssertAccounted, budgetFlags )	((void)0)
#    define	VPROF_0(name,group,assertAccounted,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()
#  endif

#  if defined( VPROF_SN_LEVEL ) && ( VPROF_SN_LEVEL >= 1 )
#	 define	VPROF_1(name,group,assertAccounted,budgetFlags)	VPROF( name )
#  else
#    define	VPROF_1(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif

#  if defined( VPROF_SN_LEVEL ) && ( VPROF_SN_LEVEL >= 2 )
#	 define	VPROF_2(name,group,assertAccounted,budgetFlags)	VPROF( name )
#  else
#    define	VPROF_2(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif

#  if defined( VPROF_SN_LEVEL ) && ( VPROF_SN_LEVEL >= 3 )
#	 define	VPROF_3(name,group,assertAccounted,budgetFlags)	VPROF( name )
#  else
#    define	VPROF_3(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif

#  if defined( VPROF_SN_LEVEL ) && ( VPROF_SN_LEVEL >= 4 )
#	 define	VPROF_4(name,group,assertAccounted,budgetFlags)	VPROF( name )
#  else
#    define	VPROF_4(name,group,assertAccounted,budgetFlags)	((void)0)
#  endif



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

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

#define VProfCode( code ) code

#endif

#ifdef VPROF_ENABLED

class PLATFORM_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;
	}

	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();

	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);		    
#endif  


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;

	int			m_iPrevL2CacheMiss;
	int			m_iCurL2CacheMiss;
	int			m_iTotalL2CacheMiss;

#ifndef _X360	
	CL2Cache	m_L2Cache;
#else  

	unsigned int m_iBitFlags;      
	CPMCData	m_PMCData;
	int			m_iPrevLoadHitStores;
	int			m_iCurLoadHitStores;
	int			m_iTotalLoadHitStores;

public:
	enum FlagBits
	{
		kRecordL2 = 0x01,
		kCPUTrace = 0x02,        
	};
	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

	int			m_nRecursions;

	unsigned	m_nCurFrameCalls;
	CCycleCount	m_CurFrameTime;

	unsigned	m_nPrevFrameCalls;
	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;
};

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,				           
										COUNTER_GROUP_TEXTURE_GLOBAL,		           
										COUNTER_GROUP_TEXTURE_PER_FRAME,		    
										COUNTER_GROUP_GRAPHICS_PER_FRAME,	        

										COUNTER_GROUP_TELEMETRY,
};

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

	void Term();

	void Start();
	void Stop();

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

#ifdef VPROF_VXCONSOLE_EXISTS
	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);          
	bool PMCEnableL2Upon(const tchar* pszNodeName, bool bRecursive = false);          
	bool PMCDisableL2Upon(const tchar* pszNodeName, bool bRecursive = false);          

	void DumpEnabledPMCNodes(void);

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

#ifdef _X360


	enum CPUTraceState
	{
		kDisabled,
		kFirstHitNode,						             
		kAllNodesInFrame_WaitingForMark,	                      
		kAllNodesInFrame_Recording,			         

		kAllNodesInFrame_WaitingForMarkMultiFrame,	                      
		kAllNodesInFrame_RecordingMultiFrame,
	};

	inline CPUTraceState GetCPUTraceMode();
	inline void SetCPUTraceEnabled(CPUTraceState enabled, bool bTraceCompleteEvent = false, int nNumFrames = -1);
	inline void IncrementMultiTraceIndex();            
	inline unsigned int GetMultiTraceIndex();    
	void CPUTraceDisableAllNodes(CVProfNode* pStartNode = NULL);          
	CVProfNode* CPUTraceEnableForNode(const tchar* pszNodeName);                
	CVProfNode* CPUTraceGetEnabledNode(CVProfNode* pStartNode = NULL);          
	const char* GetCPUTraceFilename();         
	const char* SetCPUTraceFilename(const char* filename);                
	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(char const* str);
	void MarkFrame();
	void ResetPeaks();

	void Pause();
	void Resume();
	void Reset();

	bool IsEnabled() const;
	int GetDetailLevel() const;

	bool AtRoot() const;

#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();

	void OutputReport(int type = VPRT_FULL, const tchar* pszStartNode = NULL, int budgetGroupID = -1);

	const tchar* GetBudgetGroupName(int budgetGroupID);
	int GetBudgetGroupFlags(int budgetGroupID) const;	      
	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 tchar* 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;

	void PMEInitialized(bool bInit) { m_bPMEInit = bInit; }
	void PMEEnable(bool bEnable) { m_bPMEEnabled = bEnable; }

#ifdef _X360
	bool UsePME(void) { return (CPMCData::IsInitialized() && m_bPMEEnabled); }
#elif defined( _PS3 )
	inline bool UsePME(void) { return false; }
#else
	bool UsePME(void) { return (m_bPMEInit && m_bPMEEnabled); }
#endif

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

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
	int 		m_enabled;
	bool		m_fAtRoot;         
	CVProfNode* m_pCurNode;
	CVProfNode	m_Root;
	int			m_nFrames;
	int			m_ProfileDetailLevel;
	int			m_pausedEnabledDepth;

	class CBudgetGroup
	{
	public:
		tchar* m_pName;
		int m_BudgetFlags;
	};

	CBudgetGroup* m_pBudgetGroups;
	int			m_nBudgetGroupNamesAllocated;
	int			m_nBudgetGroupNames;
	void		(*m_pNumBudgetGroupsChangedCallBack)(void);

	bool		m_bPMEInit;
	bool		m_bPMEEnabled;

	int m_Counters[MAXCOUNTERS];
	char m_CounterGroups[MAXCOUNTERS];    
	tchar* m_CounterNames[MAXCOUNTERS];
	int m_NumCounters;

#ifdef VPROF_VXCONSOLE_EXISTS
	int						m_UpdateMode;
	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
#ifdef _X360
	CPUTraceState			m_iCPUTraceEnabled;
#endif

	unsigned m_TargetThreadId;
};

PLATFORM_INTERFACE CVProfile g_VProfCurrentProfile;

PLATFORM_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;
};


#define VPROF_TEST_SPIKE( msec ) CVProfSpikeDetector UNIQUE_ID( msec )

#ifdef VPROF_VTUNE_GROUP
inline void CVProfile::PushGroup(int nGroupID)
{
	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  

#ifdef VPROF_VTUNE_GROUP
inline void CVProfile::PopGroup(void)
{
	m_GroupIDStackDepth--;
	Assert(m_GroupIDStackDepth > 0);
	if (m_GroupIDStack[m_GroupIDStackDepth] != m_GroupIDStack[m_GroupIDStackDepth + 1] &&
		VTuneGroupEnabled() &&
		m_GroupIDStack[m_GroupIDStackDepth + 1] == VTuneGroupID())
	{
		vtune(false);
	}
}
#endif  

inline CVProfile* GetVProfProfileForCurrentThread()
{
	return NULL;
}

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

private:
	bool m_bEnabled;
};

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;          
	}

	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;
}

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()
{
	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

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	VPROF_VXCONSOLE_EXISTS 
		VXProfileStart();
#endif
#ifdef _X360
		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 (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 )
		if (bAssertAccounted)
		{
			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)
#ifndef PIXBeginNamedEvent
#error PIX.h must be included if VPROF_PIX is enabled
#endif
	if (m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED)
		PIXEndNamedEvent();
#endif
	if ((!m_fAtRoot || m_enabled != 0) && InTargetThread())
	{
		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(char const* str)
{
	MarkFrame();
}

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

#ifdef _X360
		switch (GetCPUTraceMode())
		{
		case kAllNodesInFrame_WaitingForMark:
			m_iCPUTraceEnabled = kAllNodesInFrame_Recording;
			break;
		case kAllNodesInFrame_WaitingForMarkMultiFrame:
			m_iCPUTraceEnabled = kAllNodesInFrame_RecordingMultiFrame;
			break;
		case kAllNodesInFrame_Recording:
			m_iCPUTraceEnabled = kDisabled;
			Msg("Frame ended. Recording no more CPU traces\n");

			break;
		case kAllNodesInFrame_RecordingMultiFrame:
			if (--m_nFramesRemaining == 0)
			{
				m_iCPUTraceEnabled = kDisabled;
				Msg("Frames ended. Recording no more CPU traces\n");

				SpewWorstMultiFrame();
			}

			++m_nFrameCount;

			break;
		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 , int nNumFrames  )
{
	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) :
	CVProfSnMarkerScope(pszName),
	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;
	}
	void Set(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");
				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   

#endif

