Files
FC1/CryAnimation/SimpleFrameProfiler.h
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

293 lines
8.1 KiB
C++

//////////////////////////////////////////////////////////////////////////
// A simple profiler useful for collecting multiple call times per frame
// and displaying their different average statistics.
// For usage, see the bottom of the file
//////////////////////////////////////////////////////////////////////////
#ifndef _SIMPLE_FRAME_PROFILER_HDR_
#define _SIMPLE_FRAME_PROFILER_HDR_
// OBSOLETE
#if 0
#define ENABLE_FRAME_PROFILER 0
class CProfilerTimer
{
public:
static void init(); // called once
static void getTicks(__int64* nTime);
static __int64 getTicks() {__int64 nTime; getTicks(&nTime); return nTime;}
static float ticksToSeconds (__int64 nTime);
static float ticksToMilliseconds (__int64 nTime);
protected:
static __int64 g_nTicksPerSecond;
static double g_fSecondsPerTick;
static double g_fMilliSecondsPerTick;
// CPU speed, in Herz
static unsigned g_nCPUHerz;
};
extern CProfilerTimer g_ProfilerTimer;
template <typename TValue, int g_nCount>
class CProfilerTimerHistory
{
public:
CProfilerTimerHistory():
m_nTimerHistoryNext (0),
m_nTimerHistoryCount (0)
{
}
void add(float fTimer)
{
m_fTimerHistory[m_nTimerHistoryNext] = fTimer;
m_nTimerHistoryNext = (m_nTimerHistoryNext+g_nCount-1)%g_nCount;
if (m_nTimerHistoryCount < g_nCount)
++m_nTimerHistoryCount;
}
// cleans up the timer history
void clear()
{
m_nTimerHistoryNext = 0;
m_nTimerHistoryCount = 0;
}
TValue getLast()
{
if (m_nTimerHistoryCount)
return m_fTimerHistory[(m_nTimerHistoryNext+1)%g_nCount];
else
return 0;
}
// calculates average time for at most the given number of frames (less if so many unavailable)
float getAve (int nCount = g_nCount)
{
if (m_nTimerHistoryCount)
{
float fSum = 0;
if (nCount > m_nTimerHistoryCount)
nCount = m_nTimerHistoryCount;
for (int i = 1; i <= nCount; ++i)
{
fSum += m_fTimerHistory[(m_nTimerHistoryNext+i)%SIZEOF_ARRAY(m_fTimerHistory)];
}
return fSum / nCount;
}
else
return 0;
}
// calculates average time for at most the given number of frames (less if so many unavailable),
// multiplied by the Poisson function
float getAvePoisson (int nCount, float fMultiplier)
{
if (m_nTimerHistoryCount)
{
float fSum = 0, fCurrMult = 1, fSumWeight = 0;
if (nCount > m_nTimerHistoryCount)
nCount = m_nTimerHistoryCount;
for (int i = 1; i <= nCount; ++i)
{
fSum += m_fTimerHistory[(m_nTimerHistoryNext+i)%g_nCount] * fCurrMult;
fSumWeight += fCurrMult;
fCurrMult *= fMultiplier;
}
return fSum / fSumWeight;
}
else
return 0;
}
// calculates max time for at most the given number of frames (less if so many unavailable)
float getMax(int nCount=g_nCount)
{
if (m_nTimerHistoryCount)
{
float fMax;
if (nCount > m_nTimerHistoryCount)
nCount = m_nTimerHistoryCount;
for (int i = 1; i <= nCount; ++i)
{
float fCur = m_fTimerHistory[(m_nTimerHistoryNext+i)%SIZEOF_ARRAY(m_fTimerHistory)];
if (i == 1 || fCur > fMax)
fMax = fCur;
}
return fMax;
}
else
return 0;
}
// calculates min time for at most the given number of frames (less if so many unavailable)
float getMin(int nCount = g_nCount)
{
if (m_nTimerHistoryCount)
{
float fMin;
if (nCount > m_nTimerHistoryCount)
nCount = m_nTimerHistoryCount;
for (int i = 1; i <= nCount; ++i)
{
float fCur = m_fTimerHistory[(m_nTimerHistoryNext+i)%SIZEOF_ARRAY(m_fTimerHistory)];
if (i == 1 || fCur < fMin)
fMin = fCur;
}
return fMin;
}
else
return 0;
}
protected:
// the timer values for the last frames
TValue m_fTimerHistory[g_nCount];
// the current pointer in the timer history, decreases
int m_nTimerHistoryNext;
// the currently collected samples in the timer history
int m_nTimerHistoryCount;
// adds the entry to the timer history (current timer value)
};
// this is the accumulator - static object that will hold the profile info
class CSimpleFrameProfilerInfo: public CryAnimationBase
{
public:
CSimpleFrameProfilerInfo (const char* szName);
void startInterval();
void endInterval();
void startDelay();
void endDelay();
void flush();
protected:
void drawHeaderLabel ();
void drawStatistics (float fRow, float* fColor, const char* szLabel, CProfilerTimerHistory<float,64>& rProfiler);
void drawLabel(float fRow, float* fColor, const char* szText);
protected:
__int64 m_nTickTimer; // accumulates the time spent on this frame
unsigned m_nCounter; // accumulates the number of times the interval has been measured during the last frame
float m_fCounter; // accumulates the
int m_nFrame;
const char* m_szName;
int m_nIndex; // the index of this object
CProfilerTimerHistory<float,64> m_HistTime, m_HistCount;
static int g_nCount; // count of such objects ever created
};
// this is a simple class facilitating profiling within one frame:
// during the frame, it collects the profile info, and when the frame
// changes, it displays the result and flushes it to start over again
// It doesn't take the recursiveness into account
class CSimpleFrameProfiler
{
public:
CSimpleFrameProfiler (CSimpleFrameProfilerInfo* pInfo):
m_pInfo (pInfo)
{
if (pInfo)
pInfo->startInterval();
}
~CSimpleFrameProfiler ()
{
if (m_pInfo)
m_pInfo->endInterval();
}
protected:
CSimpleFrameProfilerInfo* m_pInfo;
};
// this is a class facilitating profiling within one frame, taking recursiveness
// into account:
// during the frame, it collects the profile info, and when the frame
// changes, it displays the result and flushes it to start over again
// If one block is nested into another, it delays profiling of the parent block
class CRecursiveFrameProfiler
{
// the depth of the profilers' stack
enum {nStackDepth = 32};
public:
CRecursiveFrameProfiler (CSimpleFrameProfilerInfo* pInfo):
m_pInfo (pInfo)
{
if (pInfo)
{
pInfo->startInterval();
if (g_nStackTop > 0)
g_arrStack[g_nStackTop-1]->startDelay();
g_arrStack[g_nStackTop] = this;
++g_nStackTop;
assert (g_nStackTop < nStackDepth);
}
}
~CRecursiveFrameProfiler ()
{
if (m_pInfo)
{
m_pInfo->endInterval();
--g_nStackTop;
if (g_nStackTop > 0)
g_arrStack[g_nStackTop-1]->endDelay();
assert (g_nStackTop >= 0);
}
}
protected:
void startDelay()
{
// this is only called for the profilers on the stack; only the profilers
// with non-NULL profiler info are placed on the stack
assert (m_pInfo);
m_pInfo->startDelay();
}
void endDelay()
{
// this is only called for the profilers on the stack; only the profilers
// with non-NULL profiler info are placed on the stack
assert (m_pInfo);
m_pInfo->endDelay();
}
protected:
CSimpleFrameProfilerInfo* m_pInfo;
// the profilers' stack
static CRecursiveFrameProfiler* g_arrStack[nStackDepth];
// the profiler stack pointer (the top of the stack, the stack grows up)
static int g_nStackTop;
};
// USAGE: declare in someplace:
// static DECLARE_FRAME_PROFILER(a123,"my name");
// in all the profiled blocks, put the following:
// PROFILE_FRAME(a123, true);
// set #if 0 here if you don't want profiling to be compiled in the code
#if ENABLE_FRAME_PROFILER
#include "CVars.h"
#define DECLARE_FRAME_PROFILER(id,name) extern CSimpleFrameProfilerInfo __##id##_frame_profiler
#define DEFINE_FRAME_PROFILER(id,name) CSimpleFrameProfilerInfo __##id##_frame_profiler(name)
#define PROFILE_FRAME_SELF(id) CRecursiveFrameProfiler __##id##_auto_frame_profile_locker(CryAnimationBase::GetCVars()->ca_Profile()?&__##id##_frame_profiler:NULL)
#define PROFILE_FRAME_TOTAL(id) CSimpleFrameProfiler __##id##_auto_frame_profile_locker(CryAnimationBase::GetCVars()->ca_Profile()?&__##id##_frame_profiler:NULL)
#define PROFILE_FRAME(id) PROFILE_FRAME_SELF(id)
// flushes the given profiler: makes sure it's called this frame (to draw)
#define FLUSH_PROFILER(id) do{if (CryAnimationBase::GetCVars()->ca_Profile())__##id##_frame_profiler.flush();}while(false)
#else
#define DECLARE_FRAME_PROFILER(id,name)
#define DEFINE_FRAME_PROFILER(id,name)
#define PROFILE_FRAME_SELF(id)
#define PROFILE_FRAME_TOTAL(id)
#define PROFILE_FRAME(id)
// flushes the given profiler: makes sure it's called this frame (to draw)
#define FLUSH_PROFILER(id)
#endif
#endif
#endif