Files
FlaxEngine/Source/Engine/Engine/Time.cpp
2021-01-02 14:28:49 +01:00

267 lines
6.2 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Time.h"
#include "EngineService.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Config/TimeSettings.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Physics/PhysicsSettings.h"
namespace
{
bool FixedDeltaTimeEnable;
float FixedDeltaTimeValue;
}
bool Time::_gamePaused = false;
DateTime Time::StartupTime;
float Time::UpdateFPS = 30.0f;
float Time::PhysicsFPS = 60.0f;
float Time::DrawFPS = 60.0f;
float Time::TimeScale = 1.0f;
Time::TickData Time::Update;
Time::FixedStepTickData Time::Physics;
Time::TickData Time::Draw;
Time::TickData* Time::Current = nullptr;
class TimeService : public EngineService
{
public:
TimeService()
: EngineService(TEXT("Time"), -850)
{
FixedDeltaTimeEnable = false;
FixedDeltaTimeValue = 0.0f;
#if USE_EDITOR
// Disable gameplay in Editor on startup
Time::_gamePaused = true;
#endif
}
};
TimeService TimeServiceInstance;
void Time::TickData::OnBeforeRun(float targetFps, double currentTime)
{
Time = UnscaledTime = TimeSpan::Zero();
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
LastLength = static_cast<double>(DeltaTime.Ticks) / Constants::TicksPerSecond;
LastBegin = currentTime - LastLength;
LastEnd = currentTime;
}
void Time::TickData::OnReset(float targetFps, double currentTime)
{
DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero();
LastLength = static_cast<double>(DeltaTime.Ticks) / Constants::TicksPerSecond;
LastBegin = currentTime - LastLength;
LastEnd = currentTime;
}
bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime)
{
// Check if can perform a tick
const double time = Platform::GetTimeSeconds();
double deltaTime;
if (FixedDeltaTimeEnable)
{
deltaTime = (double)FixedDeltaTimeValue;
}
else
{
deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime);
const double minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0;
if (deltaTime < minDeltaTime)
return false;
}
// Update data
Advance(time, deltaTime);
return true;
}
void Time::TickData::OnTickEnd()
{
const double time = Platform::GetTimeSeconds();
LastEnd = time;
LastLength = time - LastBegin;
}
void Time::TickData::Advance(double time, double deltaTime)
{
float timeScale = TimeScale;
if (_gamePaused)
timeScale = 0.0f;
LastBegin = time;
UnscaledDeltaTime = TimeSpan::FromSeconds(deltaTime);
UnscaledTime += UnscaledDeltaTime;
DeltaTime = TimeSpan::FromSeconds(deltaTime * (double)timeScale);
Time += DeltaTime;
TicksCount++;
}
bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime)
{
// Check if can perform a tick
double time = Platform::GetTimeSeconds();
double deltaTime, minDeltaTime;
if (FixedDeltaTimeEnable)
{
deltaTime = (double)FixedDeltaTimeValue;
minDeltaTime = deltaTime;
}
else
{
deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime);
minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0;
if (deltaTime < minDeltaTime)
return false;
}
Samples.Add(deltaTime);
// Check if last few ticks were not taking too long so it's running slowly
const bool isRunningSlowly = Samples.Average() > 1.5 * minDeltaTime;
if (!isRunningSlowly)
{
// Make steps fixed size
const double diff = deltaTime - minDeltaTime;
time -= diff;
deltaTime = minDeltaTime;
}
// Update data
Advance(time, deltaTime);
return true;
}
Time::TickData* Time::GetHighestFrequency(float& fps)
{
const auto updateFps = UpdateFPS;
const auto drawFps = DrawFPS;
const auto physicsFps = PhysicsFPS;
if (physicsFps >= drawFps && physicsFps >= updateFps)
{
fps = physicsFps;
return &Physics;
}
if (drawFps >= physicsFps && drawFps >= updateFps)
{
fps = drawFps;
return &Draw;
}
fps = updateFps;
return &Update;
}
void Time::SetGamePaused(bool value)
{
if (_gamePaused == value)
return;
_gamePaused = value;
const double time = Platform::GetTimeSeconds();
Update.OnReset(UpdateFPS, time);
Physics.OnReset(PhysicsFPS, time);
Draw.OnReset(DrawFPS, time);
}
float Time::GetDeltaTime()
{
auto* data = Current ? Current : &Update;
return data->DeltaTime.GetTotalSeconds();
}
float Time::GetGameTime()
{
auto* data = Current ? Current : &Update;
return data->Time.GetTotalSeconds();
}
float Time::GetUnscaledDeltaTime()
{
auto* data = Current ? Current : &Update;
return data->UnscaledDeltaTime.GetTotalSeconds();
}
float Time::GetUnscaledGameTime()
{
auto* data = Current ? Current : &Update;
return data->UnscaledTime.GetTotalSeconds();
}
float Time::GetTimeSinceStartup()
{
return (DateTime::Now() - StartupTime).GetTotalSeconds();
}
void Time::SetFixedDeltaTime(bool enable, float value)
{
FixedDeltaTimeEnable = enable;
FixedDeltaTimeValue = value;
}
void Time::OnBeforeRun()
{
// Initialize tick data (based on a time settings)
const double time = Platform::GetTimeSeconds();
Update.OnBeforeRun(UpdateFPS, time);
Physics.OnBeforeRun(PhysicsFPS, time);
Draw.OnBeforeRun(DrawFPS, time);
}
bool Time::OnBeginUpdate()
{
if (Update.OnTickBegin(UpdateFPS, TimeSettings::Instance()->MaxUpdateDeltaTime))
{
Current = &Update;
return true;
}
return false;
}
bool Time::OnBeginPhysics()
{
if (Physics.OnTickBegin(PhysicsFPS, PhysicsSettings::Instance()->MaxDeltaTime))
{
Current = &Physics;
return true;
}
return false;
}
bool Time::OnBeginDraw()
{
if (Draw.OnTickBegin(DrawFPS, 1.0f))
{
Current = &Draw;
return true;
}
return false;
}
void Time::OnEndUpdate()
{
Update.OnTickEnd();
Current = nullptr;
}
void Time::OnEndPhysics()
{
Physics.OnTickEnd();
Current = nullptr;
}
void Time::OnEndDraw()
{
Draw.OnTickEnd();
Current = nullptr;
}