197 lines
8.2 KiB
C++
197 lines
8.2 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
|
|
#ifndef RELIABLETIMER_H
|
|
#define RELIABLETIMER_H
|
|
|
|
#include "tier0/dbg.h"
|
|
//#include "constants.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "tier1/strtools.h"
|
|
#include "tier1/tier1.h"
|
|
|
|
#define DbgAssert Assert
|
|
#define kMILLION (1000000)
|
|
#define kTHOUSAND (1000)
|
|
|
|
// Timer class that uses QueryPerformanceCounter. This is heavier-weight than
|
|
// CFastTimer which uses rdtsc, but this is reliable on multi-core systems
|
|
// whereas CFastTimer is not.
|
|
|
|
class CReliableTimer {
|
|
public:
|
|
CReliableTimer();
|
|
void Start();
|
|
void End();
|
|
int64 GetMicroseconds();
|
|
int64 GetMilliseconds();
|
|
void SetLimit(uint64 m_cMicroSecDuration);
|
|
bool BLimitReached();
|
|
int64 CMicroSecOverage();
|
|
int64 CMicroSecLeft();
|
|
int64 CMilliSecLeft();
|
|
|
|
private:
|
|
int64 GetPerformanceCountNow();
|
|
|
|
int64 m_nPerformanceCounterStart;
|
|
int64 m_nPerformanceCounterEnd;
|
|
int64 m_nPerformanceCounterLimit;
|
|
|
|
static int64 sm_nPerformanceFrequency;
|
|
static bool sm_bUseQPC;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Records timer start time
|
|
//-----------------------------------------------------------------------------
|
|
inline void CReliableTimer::Start() {
|
|
m_nPerformanceCounterStart = GetPerformanceCountNow();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Records timer end time
|
|
//-----------------------------------------------------------------------------
|
|
inline void CReliableTimer::End() {
|
|
m_nPerformanceCounterEnd = GetPerformanceCountNow();
|
|
|
|
// enforce that we've advanced at least one cycle
|
|
if (m_nPerformanceCounterEnd < m_nPerformanceCounterStart) {
|
|
#ifdef _SERVER
|
|
if (m_nPerformanceCounterEnd + 10000 < m_nPerformanceCounterStart)
|
|
AssertMsgOnce(
|
|
false,
|
|
CDbgFmtMsg(
|
|
"CReliableTimer went backwards - start:%lld end:%lld",
|
|
m_nPerformanceCounterStart, m_nPerformanceCounterEnd)
|
|
.ToString());
|
|
#endif
|
|
m_nPerformanceCounterEnd = m_nPerformanceCounterStart + 1;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Gets microseconds elapsed between start and end
|
|
//-----------------------------------------------------------------------------
|
|
inline int64 CReliableTimer::GetMicroseconds() {
|
|
DbgAssert(m_nPerformanceCounterStart); // timer must have been started
|
|
DbgAssert(m_nPerformanceCounterEnd); // timer must have been ended
|
|
DbgAssert(0 != sm_nPerformanceFrequency); // must have calc'd performance
|
|
// counter frequency
|
|
return ((m_nPerformanceCounterEnd - m_nPerformanceCounterStart) * kMILLION /
|
|
sm_nPerformanceFrequency);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Gets microseconds elapsed between start and end
|
|
//-----------------------------------------------------------------------------
|
|
inline int64 CReliableTimer::GetMilliseconds() {
|
|
DbgAssert(m_nPerformanceCounterStart); // timer must have been started
|
|
DbgAssert(m_nPerformanceCounterEnd); // timer must have been ended
|
|
DbgAssert(0 != sm_nPerformanceFrequency); // must have calc'd performance
|
|
// counter frequency
|
|
return ((m_nPerformanceCounterEnd - m_nPerformanceCounterStart) *
|
|
kTHOUSAND / sm_nPerformanceFrequency);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets a limit on this timer that can subsequently be checked against
|
|
//-----------------------------------------------------------------------------
|
|
inline void CReliableTimer::SetLimit(uint64 cMicroSecDuration) {
|
|
DbgAssert(0 != sm_nPerformanceFrequency); // must have calc'd performance
|
|
// counter frequency
|
|
m_nPerformanceCounterStart = GetPerformanceCountNow();
|
|
m_nPerformanceCounterLimit =
|
|
m_nPerformanceCounterStart +
|
|
((cMicroSecDuration * sm_nPerformanceFrequency) / kMILLION);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns if previously set limit has been reached
|
|
//-----------------------------------------------------------------------------
|
|
inline bool CReliableTimer::BLimitReached() {
|
|
DbgAssert(m_nPerformanceCounterStart); // SetLimit must have been called
|
|
DbgAssert(m_nPerformanceCounterLimit); // SetLimit must have been called
|
|
int64 nPerformanceCountNow = GetPerformanceCountNow();
|
|
|
|
// make sure time advances
|
|
if (nPerformanceCountNow < m_nPerformanceCounterStart) {
|
|
#ifdef _SERVER
|
|
if (nPerformanceCountNow + 10000 < m_nPerformanceCounterStart)
|
|
AssertMsgOnce(
|
|
false,
|
|
CDbgFmtMsg(
|
|
"CReliableTimer went backwards - start:%lld end:%lld",
|
|
m_nPerformanceCounterStart, m_nPerformanceCounterEnd)
|
|
.ToString());
|
|
#endif
|
|
// reset the limit to be lower, to match our new clock
|
|
m_nPerformanceCounterLimit =
|
|
nPerformanceCountNow +
|
|
(m_nPerformanceCounterLimit - m_nPerformanceCounterStart);
|
|
}
|
|
|
|
return (nPerformanceCountNow >= m_nPerformanceCounterLimit);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns microseconds current time is past limit, or 0 if not past
|
|
// limit
|
|
//-----------------------------------------------------------------------------
|
|
inline int64 CReliableTimer::CMicroSecOverage() {
|
|
DbgAssert(m_nPerformanceCounterStart); // SetLimit must have been called
|
|
DbgAssert(m_nPerformanceCounterLimit); // SetLimit must have been called
|
|
int64 nPerformanceCountNow = GetPerformanceCountNow();
|
|
#ifdef _SERVER
|
|
if (nPerformanceCountNow + 10000 < m_nPerformanceCounterStart)
|
|
AssertMsgOnce(
|
|
nPerformanceCountNow >= m_nPerformanceCounterStart,
|
|
CDbgFmtMsg("CReliableTimer went backwards - start:%lld end:%lld",
|
|
m_nPerformanceCounterStart, m_nPerformanceCounterEnd)
|
|
.ToString());
|
|
#endif
|
|
int64 nPerformanceCountOver =
|
|
(nPerformanceCountNow > m_nPerformanceCounterLimit
|
|
? nPerformanceCountNow - m_nPerformanceCounterLimit
|
|
: 0);
|
|
|
|
Assert(0 != sm_nPerformanceFrequency); // must have calc'd performance
|
|
// counter frequency
|
|
return (nPerformanceCountOver * kMILLION / sm_nPerformanceFrequency);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns microseconds remaining until limit
|
|
//-----------------------------------------------------------------------------
|
|
inline int64 CReliableTimer::CMicroSecLeft() {
|
|
DbgAssert(m_nPerformanceCounterStart); // SetLimit must have been called
|
|
DbgAssert(m_nPerformanceCounterLimit); // SetLimit must have been called
|
|
int64 nPerformanceCountNow = GetPerformanceCountNow();
|
|
#ifdef _SERVER
|
|
if (nPerformanceCountNow + 10000 < m_nPerformanceCounterStart)
|
|
AssertMsgOnce(
|
|
nPerformanceCountNow >= m_nPerformanceCounterStart,
|
|
CDbgFmtMsg("CReliableTimer went backwards - start:%lld end:%lld",
|
|
m_nPerformanceCounterStart, m_nPerformanceCounterEnd)
|
|
.ToString());
|
|
#endif
|
|
int64 nPerformanceCountLeft =
|
|
(nPerformanceCountNow < m_nPerformanceCounterLimit
|
|
? m_nPerformanceCounterLimit - nPerformanceCountNow
|
|
: 0);
|
|
|
|
DbgAssert(0 != sm_nPerformanceFrequency); // must have calc'd performance
|
|
// counter frequency
|
|
return (nPerformanceCountLeft * kMILLION / sm_nPerformanceFrequency);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns milliseconds remaining until limit
|
|
//-----------------------------------------------------------------------------
|
|
inline int64 CReliableTimer::CMilliSecLeft() { return CMicroSecLeft() / 1000; }
|
|
|
|
#endif // TICKLIMITTIMER_H
|