/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTimer.h"
#include "hsUtils.h"
#if HS_BUILD_FOR_MAC
#include
#endif
#include "plTweak.h"
//
// plTimerShare - the actual worker. All process spaces should share a single
// plTimerShare to keep time synchronized across spaces.
//
plTimerShare::plTimerShare()
: fFirstTime(true),
fSysSeconds(0),
fRealSeconds(0),
fDelSysSeconds(0),
fFrameTimeInc(0.03f),
fSysTimeScale(1.f),
fTimeClampSecs(0.1f),
fSmoothingClampSecs(-1.0f),
fRunningFrameTime(false),
fClamping(false),
fResetSmooth(true),
fCurrSlot(0)
{
}
plTimerShare::~plTimerShare()
{
}
double plTimerShare::GetSeconds() const
{
hsWide ticks;
return hsTimer::GetRawTicks(&ticks)->AsDouble() / hsTimer::GetRawBase().AsDouble();
}
double plTimerShare::GetMilliSeconds() const
{
return GetSeconds() * 1.e3;
}
hsWide plTimerShare::DSecondsToRawTicks(double secs)
{
hsWide retVal;
double ticks = secs * hsTimer::GetRawBase().AsDouble();
double hi = ticks / double(65536) / double(65536);
ticks -= hi;
retVal.fHi = Int32(hi);
retVal.fLo = Int32(ticks);
return retVal;
}
double plTimerShare::RawTicksToDSeconds(const hsWide& ticks)
{
return ticks.AsDouble() / hsTimer::GetRawBase().AsDouble();
}
inline hsWide* plTimerShare::FactorInTimeZero(hsWide* ticks) const
{
if( fFirstTime )
{
fFirstTime = false;
fRawTimeZero = *ticks;
ticks->Set(0, 0);
}
else
{
ticks->Sub(&fRawTimeZero);
}
return ticks;
}
double plTimerShare::IncSysSeconds()
{
if( fRunningFrameTime )
{
fDelSysSeconds = fFrameTimeInc * fSysTimeScale;
fSysSeconds += fDelSysSeconds;
fResetSmooth = true;
}
else if( fSmoothingClampSecs >= 0 )
{
double t = GetSeconds();
hsScalar delSys = hsScalar(t - fRealSeconds);
fClamping = ( (fTimeClampSecs > 0) && (delSys > fTimeClampSecs) );
if (fClamping)
{
delSys = fTimeClampSecs;
}
delSys *= fSysTimeScale;
if( fDelSysSeconds > 0 && fDelSysSeconds < fSmoothingClampSecs )
{
const hsScalar kFrac = 0.1f;
const hsScalar kOneMinusFrac = 1.f-kFrac;
delSys *= kFrac;
delSys += fDelSysSeconds * kOneMinusFrac;
}
if (delSys > 4.0f && delSys < 5.0f)
{
//got that mysterious bug, (Win2k? certain CPU's?) try again...
#if HS_BUILD_FOR_WIN32
int count = 10;
while( delSys >= fDelSysSeconds * 2 && count > 0 )
{
fRealSeconds = t;
t = GetSeconds();
delSys = hsScalar(t - fRealSeconds);
count--;
}
#endif
}
fDelSysSeconds = delSys;
fSysSeconds += fDelSysSeconds;
fRealSeconds = t;
fResetSmooth = true;
}
else
{
double t = GetSeconds();
plCONST(int) kSmoothBuffUsed(kSmoothBuffLen);
if( fResetSmooth )
{
int i;
for( i = 0; i < kSmoothBuffUsed; i++ )
fSmoothBuff[i] = t;
fResetSmooth = false;
}
if( ++fCurrSlot >= kSmoothBuffUsed )
fCurrSlot = 0;
fSmoothBuff[fCurrSlot] = t;
double avg = 0;
int j;
for( j = 0; j < kSmoothBuffUsed; j++ )
{
avg += fSmoothBuff[j];
}
avg /= double(kSmoothBuffUsed);
plCONST(hsScalar) kMaxSmoothable(0.15f);
fDelSysSeconds = hsScalar(avg - fRealSeconds) * fSysTimeScale;
if( fDelSysSeconds > kMaxSmoothable * fSysTimeScale )
{
avg = t;
fDelSysSeconds = hsScalar(avg - fRealSeconds) * fSysTimeScale;
fResetSmooth = true;
}
fSysSeconds += fDelSysSeconds;
fRealSeconds = avg;
}
return fSysSeconds;
}
void plTimerShare::SetRealTime(hsBool realTime)
{
fRunningFrameTime = !realTime;
if( realTime )
{
fRealSeconds = GetSeconds();
}
}
#if HS_BUILD_FOR_WIN32
#include
hsWide* plTimerShare::GetRawTicks(hsWide* ticks) const
{
LARGE_INTEGER large;
if (::QueryPerformanceCounter(&large))
{
ticks->Set(large.HighPart, large.LowPart);
}
else
{
ticks->Set(0, ::GetTickCount());
}
return FactorInTimeZero(ticks);
}
hsWide hsTimer::IInitRawBase()
{
hsWide base;
LARGE_INTEGER large;
if (::QueryPerformanceFrequency(&large))
base.Set(large.HighPart, large.LowPart);
else
base.Set(0, 1000);
return base;
}
#elif HS_BUILD_FOR_MAC
#include
#include
//#define HS_USE_TICKCOUNT
hsWide* plTimerShare::GetRawTicks(hsWide* ticks)
{
#ifndef HS_USE_TICKCOUNT
UnsignedWide ns = AbsoluteToNanoseconds(UpTime());
ticks->Set(ns.hi, ns.lo);
#else
ticks->Set(0, TickCount());
#endif
return FactorInTimeZero(ticks);
}
hsWide plTimerShare::IInitRawBase()
{
hsWide base;
#ifndef HS_USE_TICKCOUNT
base.Set(0, 1000000000L);
#else
base.Set(0, 60);
#endif
return base;
}
#elif HS_BUILD_FOR_UNIX
#include
#define kMicroSecondsUnit 1000000
static UInt32 gBaseTime = 0;
hsWide* plTimerShare::GetRawTicks(hsWide* ticks) const
{
timeval tv;
(void)::gettimeofday(&tv, nil);
if (gBaseTime == 0)
gBaseTime = tv.tv_sec;
ticks->Mul(tv.tv_sec - gBaseTime, kMicroSecondsUnit)->Add(tv.tv_usec);
return ticks;
}
hsWide hsTimer::IInitRawBase()
{
hsWide base;
base.Set(0, kMicroSecondsUnit);
return base;
}
#elif HS_BUILD_FOR_PS2
extern unsigned long psTimerGetCount();
//#define kTickMul (150000000) // kTickMul/kTickDiv :: 4577.636719
#define kTickMul (100000000) // kTickMul/kTickDiv :: 3051.757813 // for debugger
#define kTickDiv (256*128)
hsWide* plTimerShare::GetRawTicks(hsWide* ticks)
{
unsigned long t= psTimerGetCount();
ticks->Set( (Int32)(t>>32), (Int32)(t&((1ul<<32)-1)));
return ticks;
}
hsWide plTimerShare::IInitRawBase()
{
hsWide base;
base.Set(0, kTickMul/kTickDiv );
return base;
}
#endif
//
// hsTimer - thin static interface to plTimerShare. Also keeps a couple of
// constants.
//
static plTimerShare staticTimer;
plTimerShare* hsTimer::fTimer = &staticTimer; // until overridden.
const double hsTimer::fPrecTicksPerSec = hsTimer::GetPrecTicksPerSec();
const hsWide hsTimer::fRawBase = hsTimer::IInitRawBase();
void hsTimer::SetTheTimer(plTimerShare* timer)
{
fTimer = timer;
}
///////////////////////////
// Precision timer routines
// These remain as statics
// since they are stateless
// anyway.
///////////////////////////
double hsTimer::GetPrecTicksPerSec()
{
#if HS_BUILD_FOR_WIN32
LARGE_INTEGER freq;
if( !QueryPerformanceFrequency(&freq) )
{
return 1000.f;
}
return ((double) freq.LowPart);
#endif
#if HS_BUILD_FOR_MAC
return 1000.f;
#endif
#if HS_BUILD_FOR_PS2
return 1000.f;
#endif
return 1;
}
UInt32 hsTimer::GetPrecTickCount()
{
#if HS_BUILD_FOR_WIN32
LARGE_INTEGER ti;
if( !QueryPerformanceCounter(&ti) )
return GetTickCount();
return ti.LowPart;
#endif
#if HS_BUILD_FOR_MACPPC
return hsTimer::GetMSeconds();
#endif
#if HS_BUILD_FOR_PS2
return hsTimer::GetMSeconds();
#endif
}
UInt32 hsTimer::PrecSecsToTicks(hsScalar secs)
{
return (UInt32)(((double)secs) * fPrecTicksPerSec);
}
double hsTimer::PrecTicksToSecs(UInt32 ticks)
{
return ((double)ticks) / fPrecTicksPerSec;
}
double hsTimer::PrecTicksToHz(UInt32 ticks)
{
return fPrecTicksPerSec / ((double)ticks);
}
UInt64 hsTimer::GetFullTickCount()
{
#if HS_BUILD_FOR_WIN32
LARGE_INTEGER ticks;
QueryPerformanceCounter(&ticks);
return ticks.QuadPart;
#else
return 0;
#endif
}
float hsTimer::FullTicksToMs(UInt64 ticks)
{
#ifdef HS_BUILD_FOR_WIN32
static UInt64 ticksPerTenthMs = 0;
if (ticksPerTenthMs == 0)
{
LARGE_INTEGER perfFreq;
QueryPerformanceFrequency(&perfFreq);
ticksPerTenthMs = perfFreq.QuadPart / 10000;
}
return float(ticks / ticksPerTenthMs) / 10.f;
#else
return 0.f;
#endif
}