You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
20 KiB
C++
730 lines
20 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "CrySystem_precompiled.h"
|
|
#include "Timer.h"
|
|
#include <time.h>
|
|
#include <ISystem.h>
|
|
#include <IConsole.h>
|
|
#include <ILog.h>
|
|
#include <ISerialize.h>
|
|
/////////////////////////////////////////////////////
|
|
|
|
#ifdef WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include "windows.h"
|
|
#include "Mmsystem.h"
|
|
#endif
|
|
|
|
//#define PROFILING 1
|
|
#ifdef PROFILING
|
|
static int64 g_lCurrentTime = 0;
|
|
#endif
|
|
|
|
//! Profile smoothing time in seconds (original default was .8 / log(10) ~= .35 s)
|
|
static const float fDEFAULT_PROFILE_SMOOTHING = 1.0f;
|
|
|
|
|
|
|
|
#define DEFAULT_FRAME_SMOOTHING 1
|
|
|
|
/////////////////////////////////////////////////////
|
|
CTimer::CTimer()
|
|
{
|
|
// Default CVar values
|
|
m_fixed_time_step = 0;
|
|
m_max_time_step = 0.25f;
|
|
m_cvar_time_scale = 1.0f;
|
|
m_TimeSmoothing = DEFAULT_FRAME_SMOOTHING; // note: frame numbers (old version - commented out) are not used but is based on time
|
|
m_TimeDebug = 0;
|
|
|
|
m_profile_smooth_time = fDEFAULT_PROFILE_SMOOTHING;
|
|
m_profile_weighting = 1;
|
|
|
|
// Persistant state
|
|
m_bEnabled = true;
|
|
//m_fixedTimeModeEnabled = false;
|
|
m_nFrameCounter = 0;
|
|
|
|
m_lTicksPerSec = CryGetTicksPerSec();
|
|
m_fSecsPerTick = 1.0 / m_lTicksPerSec;
|
|
|
|
m_fAverageFrameTime = 1.0f / 30.0f;
|
|
for (int i = 0; i < MAX_FRAME_AVERAGE; i++)
|
|
{
|
|
m_arrFrameTimes[i] = m_fAverageFrameTime;
|
|
}
|
|
|
|
m_fAvgFrameTime = 0.0f;
|
|
m_fProfileBlend = 1.0f;
|
|
m_fSmoothTime = 0;
|
|
|
|
m_totalTimeScale = 1.0f;
|
|
ClearTimeScales();
|
|
|
|
ResetTimer();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
bool CTimer::Init()
|
|
{
|
|
// if game code was accessing them by name there was something wrong anyway
|
|
|
|
REGISTER_CVAR2("t_Smoothing", &m_TimeSmoothing, DEFAULT_FRAME_SMOOTHING, 0,
|
|
"time smoothing\n"
|
|
"0=off, 1=on");
|
|
|
|
REGISTER_CVAR2("t_FixedStep", &m_fixed_time_step, 0, VF_NET_SYNCED | VF_DEV_ONLY,
|
|
"Game updated with this fixed frame time\n"
|
|
"0=off, number specifies the frame time in seconds\n"
|
|
"e.g. 0.033333(30 fps), 0.1(10 fps), 0.01(100 fps)");
|
|
|
|
REGISTER_CVAR2("t_MaxStep", &m_max_time_step, 0.25f, 0,
|
|
"Game systems clamped to this frame time");
|
|
|
|
// todo: reconsider exposing that as cvar (negative time, same value is used by Trackview, better would be another value multipled with the internal one)
|
|
REGISTER_CVAR2("t_Scale", &m_cvar_time_scale, 1.0f, VF_NET_SYNCED | VF_DEV_ONLY,
|
|
"Game time scaled by this - for variable slow motion");
|
|
|
|
REGISTER_CVAR2("t_Debug", &m_TimeDebug, 0, 0, "Timer debug: 0 = off, 1 = events, 2 = verbose");
|
|
|
|
// -----------------
|
|
|
|
REGISTER_CVAR2("profile_smooth", &m_profile_smooth_time, fDEFAULT_PROFILE_SMOOTHING, 0,
|
|
"Profiler exponential smoothing interval (seconds)");
|
|
|
|
REGISTER_CVAR2("profile_weighting", &m_profile_weighting, 1, 0,
|
|
"Profiler smoothing mode: 0 = legacy, 1 = average, 2 = peak weighted, 3 = peak hold");
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetFrameTime(ETimer which) const
|
|
{
|
|
float result = 0.0f;
|
|
if (m_bEnabled)
|
|
{
|
|
if (which != ETIMER_GAME || !m_bGameTimerPaused)
|
|
{
|
|
if (which == ETIMER_UI)
|
|
{
|
|
result = m_fRealFrameTime;
|
|
}
|
|
else
|
|
{
|
|
result = m_fFrameTime;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetCurrTime(ETimer which) const
|
|
{
|
|
assert(which >= 0 && which < ETIMER_LAST && "Bad timer index");
|
|
return m_CurrTime[which].GetSeconds();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetRealFrameTime() const
|
|
{
|
|
return m_bEnabled ? m_fRealFrameTime : 0.0f;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetTimeScale() const
|
|
{
|
|
return m_cvar_time_scale * m_totalTimeScale;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetTimeScale(uint32 channel) const
|
|
{
|
|
assert(channel < NUM_TIME_SCALE_CHANNELS);
|
|
if (channel >= NUM_TIME_SCALE_CHANNELS)
|
|
{
|
|
return GetTimeScale();
|
|
}
|
|
return m_cvar_time_scale * m_timeScaleChannels[channel];
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::SetTimeScale(float scale, uint32 channel /* = 0 */)
|
|
{
|
|
assert(channel < NUM_TIME_SCALE_CHANNELS);
|
|
if (channel >= NUM_TIME_SCALE_CHANNELS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const float currentScale = m_timeScaleChannels[channel];
|
|
|
|
if (scale != currentScale)
|
|
{
|
|
// Need to adjust previous frame times for time scale to have immediate effect
|
|
const float adjustFactor = scale / currentScale;
|
|
for (uint32 i = 0; i < MAX_FRAME_AVERAGE; ++i)
|
|
{
|
|
m_arrFrameTimes[i] *= adjustFactor;
|
|
}
|
|
|
|
// Update total time scale immediately
|
|
m_totalTimeScale *= adjustFactor;
|
|
}
|
|
|
|
m_timeScaleChannels[channel] = scale;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::ClearTimeScales()
|
|
{
|
|
if (m_totalTimeScale != 1.0f)
|
|
{
|
|
// Need to adjust previous frame times for time scale to have immediate effect
|
|
const float adjustFactor = 1.0f / m_totalTimeScale;
|
|
for (uint32 i = 0; i < MAX_FRAME_AVERAGE; ++i)
|
|
{
|
|
m_arrFrameTimes[i] *= adjustFactor;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < NUM_TIME_SCALE_CHANNELS; ++i)
|
|
{
|
|
m_timeScaleChannels[i] = 1.0f;
|
|
}
|
|
m_totalTimeScale = 1.0f;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetAsyncCurTime()
|
|
{
|
|
//int64 llNow = CryGetTicks() - m_lBaseTime_Async;
|
|
int64 llNow = CryGetTicks() - m_lBaseTime;
|
|
return TicksToSeconds(llNow);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
float CTimer::GetFrameRate()
|
|
{
|
|
// Use real frame time.
|
|
if (m_fRealFrameTime != 0.f)
|
|
{
|
|
return 1.f / m_fRealFrameTime;
|
|
}
|
|
return 0.f;
|
|
}
|
|
|
|
void CTimer::UpdateBlending()
|
|
{
|
|
// Accumulate smoothing time up to specified max.
|
|
float fFrameTime = m_fRealFrameTime;
|
|
m_fSmoothTime = min(m_fSmoothTime + fFrameTime, m_profile_smooth_time);
|
|
|
|
if (m_fSmoothTime <= fFrameTime)
|
|
{
|
|
m_fAvgFrameTime = fFrameTime;
|
|
m_fProfileBlend = 1.f;
|
|
return;
|
|
}
|
|
|
|
if (m_profile_weighting <= 2)
|
|
{
|
|
// Update average frame time.
|
|
if (m_fSmoothTime < m_fAvgFrameTime)
|
|
{
|
|
m_fAvgFrameTime = m_fSmoothTime;
|
|
}
|
|
m_fAvgFrameTime *= m_fSmoothTime / (m_fSmoothTime - fFrameTime + m_fAvgFrameTime);
|
|
|
|
if (m_profile_weighting == 1)
|
|
{
|
|
// Weight all frames equally.
|
|
m_fProfileBlend = m_fAvgFrameTime / m_fSmoothTime;
|
|
}
|
|
else
|
|
{
|
|
// Weight frames by time.
|
|
m_fProfileBlend = fFrameTime / m_fSmoothTime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Decay avg frame time, set as new peak.
|
|
m_fAvgFrameTime *= 1.f - fFrameTime / m_fSmoothTime;
|
|
if (fFrameTime > m_fAvgFrameTime)
|
|
{
|
|
m_fAvgFrameTime = fFrameTime;
|
|
m_fProfileBlend = 1.f;
|
|
}
|
|
else
|
|
{
|
|
m_fProfileBlend = 0.f;
|
|
}
|
|
}
|
|
}
|
|
|
|
float CTimer::GetProfileFrameBlending(float* pfBlendTime, int* piBlendMode)
|
|
{
|
|
if (piBlendMode)
|
|
{
|
|
*piBlendMode = m_profile_weighting;
|
|
}
|
|
if (pfBlendTime)
|
|
{
|
|
*pfBlendTime = m_fSmoothTime;
|
|
}
|
|
return m_fProfileBlend;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::RefreshGameTime(int64 curTime)
|
|
{
|
|
assert(curTime + m_lOffsetTime >= 0);
|
|
m_CurrTime[ETIMER_GAME].SetSeconds(TicksToSeconds(curTime + m_lOffsetTime));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::RefreshUITime(int64 curTime)
|
|
{
|
|
assert(curTime >= 0);
|
|
m_CurrTime[ETIMER_UI].SetSeconds(TicksToSeconds(curTime));
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::UpdateOnFrameStart()
|
|
{
|
|
if (!m_bEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//int64 now;
|
|
|
|
//if (m_fixedTimeModeEnabled)
|
|
//{
|
|
// m_nFrameCounter++;
|
|
// m_fRealFrameTime = m_fFrameTime = m_fixedTimeModeStep;
|
|
// m_lCurrentTime += m_fixedTimeModeStep*m_lTicksPerSec;
|
|
// now = m_lCurrentTime;
|
|
//}
|
|
//else
|
|
//{
|
|
// On Windows before Vista, frequency can change (even though it should be impossible),
|
|
// See also: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
|
|
// Win2000, WinXP: Uses RDTSC, which may not be monotonic across all cores (a bug), costs in the order of 10~100 cycles (cheap).
|
|
// WinVista: Uses HPET or ACPI timer (a kernel call, and much more expensive than RDTSC, but it's not bugged).
|
|
// Win7+: RDTSC if the CPU feature bit for monotonic is set, HPET or ACPI otherwise (not bugged).
|
|
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
|
|
if ((m_nFrameCounter & 127) == 0)
|
|
{
|
|
// every bunch of frames, check frequency to adapt to
|
|
// CPU power management clock rate changes
|
|
LARGE_INTEGER TTicksPerSec;
|
|
if (QueryPerformanceFrequency(&TTicksPerSec))
|
|
{
|
|
// if returns false, no performance counter is available
|
|
m_lTicksPerSec = TTicksPerSec.QuadPart;
|
|
m_fSecsPerTick = 1.0 / m_lTicksPerSec;
|
|
}
|
|
}
|
|
|
|
m_nFrameCounter++;
|
|
#endif
|
|
//}
|
|
|
|
#ifdef PROFILING
|
|
m_fRealFrameTime = m_fFrameTime = 0.020f; // 20ms = 50fps
|
|
g_lCurrentTime += (int)(m_fFrameTime * (float)(CTimeValue::TIMEVALUE_PRECISION));
|
|
m_lLastTime = g_lCurrentTime;
|
|
RefreshGameTime(m_lLastTime);
|
|
RefreshUITime(m_lLastTime);
|
|
return;
|
|
#endif
|
|
|
|
if (m_fixed_time_step < 0.0f)
|
|
{
|
|
// Enforce real framerate by sleeping.
|
|
const int64 elapsedTicks = CryGetTicks() - m_lBaseTime - m_lLastTime;
|
|
const int64 minTicks = SecondsToTicks(-m_fixed_time_step);
|
|
if (elapsedTicks < minTicks)
|
|
{
|
|
const int64 ms = (minTicks - elapsedTicks) * 1000 / m_lTicksPerSec;
|
|
CrySleep((unsigned int)ms);
|
|
}
|
|
}
|
|
|
|
const int64 now = CryGetTicks();
|
|
assert(now + 1 >= m_lBaseTime && "Invalid base time"); //+1 margin because QPC may be one off across cores
|
|
|
|
m_fRealFrameTime = TicksToSeconds(now - m_lBaseTime - m_lLastTime);
|
|
|
|
if (0.0f != m_fixed_time_step)
|
|
{
|
|
// Apply fixed_time_step
|
|
m_fFrameTime = abs(m_fixed_time_step);
|
|
}
|
|
else
|
|
{
|
|
// Clamp to max_time_step
|
|
m_fFrameTime = min(m_fRealFrameTime, m_max_time_step);
|
|
}
|
|
|
|
// Dilate time.
|
|
m_fFrameTime *= GetTimeScale();
|
|
|
|
if (m_TimeSmoothing > 0)
|
|
{
|
|
m_fFrameTime = GetAverageFrameTime();
|
|
}
|
|
|
|
// Time can only go forward.
|
|
if (m_fFrameTime < 0.0f)
|
|
{
|
|
m_fFrameTime = 0.0f;
|
|
}
|
|
if (m_fRealFrameTime < 0.0f)
|
|
{
|
|
m_fRealFrameTime = 0.0;
|
|
}
|
|
|
|
// Adjust the base time so that time actually seems to have moved forward m_fFrameTime
|
|
const int64 frameTicks = SecondsToTicks(m_fFrameTime);
|
|
const int64 realTicks = SecondsToTicks(m_fRealFrameTime);
|
|
m_lBaseTime += realTicks - frameTicks;
|
|
if (m_lBaseTime > now)
|
|
{
|
|
// Guard against rounding errors due to float <-> int64 precision
|
|
assert(m_lBaseTime - now <= 10 && "Bad base time or adjustment, too much difference for a rounding error");
|
|
m_lBaseTime = now;
|
|
}
|
|
const int64 currentTime = now - m_lBaseTime;
|
|
|
|
assert(fabsf(TicksToSeconds(currentTime - m_lLastTime) - m_fFrameTime) < 0.01f && "Bad calculation");
|
|
assert(currentTime >= m_lLastTime && "Bad adjustment in previous frame");
|
|
assert(currentTime + m_lOffsetTime >= 0 && "Sum of game time is negative");
|
|
|
|
// Update timers
|
|
RefreshUITime(currentTime);
|
|
if (!m_bGameTimerPaused)
|
|
{
|
|
RefreshGameTime(currentTime);
|
|
}
|
|
|
|
m_lLastTime = currentTime;
|
|
|
|
UpdateBlending();
|
|
|
|
if (m_TimeDebug > 1)
|
|
{
|
|
CryLogAlways("[CTimer]: Cur=%lld Now=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)currentTime, (long long)now, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//-- average frame-times to avoid stalls and peaks in framerate
|
|
//-- note that is is time-base averaging and not frame-based
|
|
//------------------------------------------------------------------------
|
|
float CTimer::GetAverageFrameTime()
|
|
{
|
|
f32 LastAverageFrameTime = m_fAverageFrameTime;
|
|
f32 FrameTime = m_fFrameTime;
|
|
|
|
uint32 numFT = MAX_FRAME_AVERAGE;
|
|
for (int32 i = (numFT - 2); i > -1; i--)
|
|
{
|
|
m_arrFrameTimes[i + 1] = m_arrFrameTimes[i];
|
|
}
|
|
|
|
if (FrameTime > 0.4f)
|
|
{
|
|
FrameTime = 0.4f;
|
|
}
|
|
if (FrameTime < 0.0f)
|
|
{
|
|
FrameTime = 0.0f;
|
|
}
|
|
m_arrFrameTimes[0] = FrameTime;
|
|
|
|
//get smoothed frame
|
|
uint32 avrg_ftime = 1;
|
|
if (LastAverageFrameTime)
|
|
{
|
|
avrg_ftime = uint32(0.25f / LastAverageFrameTime + 0.5f); //average the frame-times for a certain time-period (sec)
|
|
if (avrg_ftime > numFT)
|
|
{
|
|
avrg_ftime = numFT;
|
|
}
|
|
if (avrg_ftime < 1)
|
|
{
|
|
avrg_ftime = 1;
|
|
}
|
|
}
|
|
|
|
f32 AverageFrameTime = 0;
|
|
for (uint32 i = 0; i < avrg_ftime; i++)
|
|
{
|
|
AverageFrameTime += m_arrFrameTimes[i];
|
|
}
|
|
AverageFrameTime /= avrg_ftime;
|
|
|
|
//don't smooth if we pause the game
|
|
if (FrameTime < 0.0001f)
|
|
{
|
|
AverageFrameTime = FrameTime;
|
|
}
|
|
|
|
m_fAverageFrameTime = AverageFrameTime;
|
|
return AverageFrameTime;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::ResetTimer()
|
|
{
|
|
m_lBaseTime = CryGetTicks();
|
|
//m_lBaseTime_Async = CryGetTicks();
|
|
m_lLastTime = 0;
|
|
m_lOffsetTime = 0;
|
|
|
|
m_fFrameTime = 0.0f;
|
|
m_fRealFrameTime = 0.0f;
|
|
|
|
RefreshGameTime(0);
|
|
RefreshUITime(0);
|
|
|
|
m_bGameTimerPaused = false;
|
|
m_lGameTimerPausedTime = 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::EnableTimer(bool bEnable)
|
|
{
|
|
m_bEnabled = bEnable;
|
|
}
|
|
|
|
bool CTimer::IsTimerEnabled() const
|
|
{
|
|
return m_bEnabled;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
CTimeValue CTimer::GetAsyncTime() const
|
|
{
|
|
int64 llNow = CryGetTicks();
|
|
double fConvert = CTimeValue::TIMEVALUE_PRECISION * m_fSecsPerTick;
|
|
return CTimeValue(int64(llNow * fConvert));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
void CTimer::Serialize(TSerialize ser)
|
|
{
|
|
// cannot change m_lBaseTime, as this is used for async time (which shouldn't be affected by save games)
|
|
if (ser.IsWriting())
|
|
{
|
|
int64 currentGameTime = m_lLastTime + m_lOffsetTime;
|
|
|
|
ser.Value("curTime", currentGameTime);
|
|
ser.Value("ticksPerSecond", m_lTicksPerSec);
|
|
}
|
|
else
|
|
{
|
|
int64 ticksPerSecond = 1, curTime = 1;
|
|
ser.Value("curTime", curTime);
|
|
ser.Value("ticksPerSecond", ticksPerSecond);
|
|
|
|
// Adjust curTime for ticksPerSecond on this machine.
|
|
// Some precision will be lost if the frequencies are not identical.
|
|
const double multiplier = (double)m_lTicksPerSec / (double)ticksPerSecond;
|
|
curTime = (int64)((double)curTime * multiplier);
|
|
|
|
SetOffsetToMatchGameTime(curTime);
|
|
|
|
if (m_TimeDebug)
|
|
{
|
|
const int64 now = CryGetTicks();
|
|
CryLogAlways("[CTimer]: Serialize: Last=%lld Now=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)m_lLastTime, (long long)now, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
|
|
}
|
|
}
|
|
}
|
|
|
|
//! try to pause/unpause a timer
|
|
// returns true if successfully paused/unpaused, false otherwise
|
|
bool CTimer::PauseTimer(ETimer which, bool bPause)
|
|
{
|
|
if (which != ETIMER_GAME)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (m_bGameTimerPaused == bPause)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_bGameTimerPaused = bPause;
|
|
|
|
if (bPause)
|
|
{
|
|
m_lGameTimerPausedTime = m_lLastTime + m_lOffsetTime;
|
|
if (m_TimeDebug)
|
|
{
|
|
CryLogAlways("[CTimer]: Pausing ON: Last=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)m_lLastTime, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetOffsetToMatchGameTime(m_lGameTimerPausedTime);
|
|
m_lGameTimerPausedTime = 0;
|
|
if (m_TimeDebug)
|
|
{
|
|
CryLogAlways("[CTimer]: Pausing OFF: Last=%lld Off=%lld Async=%f CurrTime=%f UI=%f", (long long)m_lLastTime, (long long)m_lOffsetTime, GetAsyncCurTime(), GetCurrTime(ETIMER_GAME), GetCurrTime(ETIMER_UI));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//! determine if a timer is paused
|
|
// returns true if paused, false otherwise
|
|
bool CTimer::IsTimerPaused(ETimer which)
|
|
{
|
|
if (which != ETIMER_GAME)
|
|
{
|
|
return false;
|
|
}
|
|
return m_bGameTimerPaused;
|
|
}
|
|
|
|
//! try to set a timer
|
|
// return true if successful, false otherwise
|
|
bool CTimer::SetTimer(ETimer which, float timeInSeconds)
|
|
{
|
|
if (which != ETIMER_GAME)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SetOffsetToMatchGameTime(SecondsToTicks(timeInSeconds));
|
|
return true;
|
|
}
|
|
|
|
ITimer* CTimer::CreateNewTimer()
|
|
{
|
|
return new CTimer();
|
|
}
|
|
|
|
void CTimer::SecondsToDateUTC(time_t inTime, struct tm& outDateUTC)
|
|
{
|
|
#ifdef AZ_COMPILER_MSVC
|
|
gmtime_s(&outDateUTC, &inTime);
|
|
#else
|
|
outDateUTC = *gmtime(&inTime);
|
|
#endif
|
|
}
|
|
|
|
#if defined (WIN32) || defined(WIN64)
|
|
time_t gmt_to_local_win32(void)
|
|
{
|
|
TIME_ZONE_INFORMATION tzinfo;
|
|
DWORD dwStandardDaylight;
|
|
long bias;
|
|
|
|
dwStandardDaylight = GetTimeZoneInformation(&tzinfo);
|
|
bias = tzinfo.Bias;
|
|
|
|
if (dwStandardDaylight == TIME_ZONE_ID_STANDARD)
|
|
{
|
|
bias += tzinfo.StandardBias;
|
|
}
|
|
|
|
if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT)
|
|
{
|
|
bias += tzinfo.DaylightBias;
|
|
}
|
|
|
|
return (-bias * 60);
|
|
}
|
|
#endif
|
|
|
|
time_t CTimer::DateToSecondsUTC(struct tm& inDate)
|
|
{
|
|
#if defined (WIN32)
|
|
return mktime(&inDate) + gmt_to_local_win32();
|
|
#elif defined (LINUX)
|
|
#if defined (HAVE_TIMEGM)
|
|
// return timegm(&inDate);
|
|
#else
|
|
// craig: temp disabled the +tm.tm_gmtoff because i can't see the intention here
|
|
// and it doesn't compile anymore
|
|
// alexl: tm_gmtoff is the offset to greenwhich mean time, whereas mktime uses localtime
|
|
// but not all linux distributions have it...
|
|
return mktime(&inDate) /*+ tm.tm_gmtoff*/;
|
|
#endif
|
|
#else
|
|
return mktime(&inDate);
|
|
#endif
|
|
}
|
|
|
|
void CTimer::EnableFixedTimeMode([[maybe_unused]] bool enable, [[maybe_unused]] float timeStep)
|
|
{
|
|
//if (enable)
|
|
//{
|
|
// m_fixedTimeModeEnabled = true;
|
|
// m_fixedTimeModeStep = timeStep;
|
|
|
|
// m_lBaseTime =0;
|
|
// m_lBaseTime_Async = 0;
|
|
// m_lLastTime = m_lCurrentTime = 0;
|
|
// m_fRealFrameTime = m_fFrameTime = timeStep;
|
|
// RefreshGameTime(m_lCurrentTime);
|
|
// RefreshUITime(m_lCurrentTime);
|
|
// m_lForcedGameTime = -1;
|
|
// m_bGameTimerPaused = false;
|
|
// m_lGameTimerPausedTime = 0;
|
|
//}
|
|
//else
|
|
//{
|
|
// m_fixedTimeModeEnabled = false;
|
|
// ResetTimer();
|
|
//}
|
|
}
|
|
|
|
void CTimer::SetOffsetToMatchGameTime(int64 ticks)
|
|
{
|
|
const int64 previousOffset = m_lOffsetTime;
|
|
const float previousGameTime = GetCurrTime(ETIMER_GAME);
|
|
|
|
m_lOffsetTime = ticks - m_lLastTime;
|
|
RefreshGameTime(m_lLastTime);
|
|
|
|
if (m_bGameTimerPaused)
|
|
{
|
|
// On un-pause, we will restore the specified time.
|
|
// If we don't do this, the un-pause will over-write the offset again.
|
|
m_lGameTimerPausedTime = ticks;
|
|
}
|
|
|
|
if (m_TimeDebug)
|
|
{
|
|
CryLogAlways("[CTimer] SetOffset: Offset %lld -> %lld, GameTime %f -> %f", (long long)previousOffset, (long long)m_lOffsetTime, GetCurrTime(ETIMER_GAME), previousGameTime);
|
|
}
|
|
}
|
|
|
|
int64 CTimer::SecondsToTicks(double seconds) const
|
|
{
|
|
return (int64)(seconds * (double)m_lTicksPerSec);
|
|
}
|