diff options
Diffstat (limited to 'Runtime/Input/TimeManager.cpp')
| -rw-r--r-- | Runtime/Input/TimeManager.cpp | 489 | 
1 files changed, 489 insertions, 0 deletions
| diff --git a/Runtime/Input/TimeManager.cpp b/Runtime/Input/TimeManager.cpp new file mode 100644 index 0000000..faadada --- /dev/null +++ b/Runtime/Input/TimeManager.cpp @@ -0,0 +1,489 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "TimeManager.h" +#include <limits> +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Math/FloatConversion.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Misc/ReproductionLog.h" +#include "Runtime/Threads/Thread.h" +#if SUPPORT_REPRODUCE_LOG +#include <fstream> +#endif + +using namespace std; + + +#define DEBUG_TIME_MANAGER 0 + + +const float kMaximumDeltaTime = 1.0F / 3.0F; +const float kStartupDeltaTime = 0.02F; +const float kNewDeltaTimeWeight = 0.2F; // for smoothing + +float CalcInvDeltaTime (float dt) +{ +	if (dt > kMinimumDeltaTime) +		return 1.0F / dt; +	else +		return 1.0F; +} + +TimeManager::TimeHolder::TimeHolder() +:	m_CurFrameTime(0) +,	m_LastFrameTime(0) +,	m_DeltaTime(0) +,	m_SmoothDeltaTime(0) +,	m_SmoothingWeight(0) +,	m_InvDeltaTime(0) +{ +} + +TimeManager::TimeManager (MemLabelId label, ObjectCreationMode mode) +:	Super(label, mode) +,	m_CullFrameCount(0) +{ +	m_SetTimeManually = false; +	m_UseFixedTimeStep = false; + +	m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime; + +	m_TimeScale = 1.0F; +	m_FixedTime.m_DeltaTime = 0.02F; +	m_MaximumTimestep = kMaximumDeltaTime; +	m_LastSyncEnd = 0; +	ResetTime (); +} + +TimeManager::~TimeManager () {} + +inline void CalcSmoothDeltaTime (TimeManager::TimeHolder& time) +{ +	// If existing weight is zero, don't take existing value into account +	time.m_SmoothingWeight *= (1.0F - kNewDeltaTimeWeight); +	time.m_SmoothingWeight += kNewDeltaTimeWeight; +	// As confidence in smoothed value increases the divisor goes towards 1 +	float normalized = kNewDeltaTimeWeight / time.m_SmoothingWeight; +	time.m_SmoothDeltaTime = Lerp (time.m_SmoothDeltaTime, time.m_DeltaTime, normalized); +} + +void TimeManager::SetPause(bool pause) +{ +	m_FirstFrameAfterPause = true; +} + +void TimeManager::Sync (float framerate) +{ +	double time = GetTimeSinceStartup (); +	// wait for enough time to pass for the requested framerate +	if (framerate > 0) +	{ +		// Wait a bit less (0.1ms), to accomodate for small fluctuations (gives much better result in webplayers) +		double frameTime = 1.0/(double)framerate - 0.0001; +		if (!CompareApproximately(time, m_LastSyncEnd) && time - m_LastSyncEnd < frameTime) +		{ +#if SUPPORT_THREADS +			Thread::Sleep(frameTime - (time-m_LastSyncEnd)); +#endif +			int i = 0; +			double start = GetTimeSinceStartup(); +			// do the last adjustment with a busy wait +			do +			{ +				time = GetTimeSinceStartup(); +				if (++i >=  1000) +				{ +					// When using PerfHUD ES together with the NVIDIA time extension +					// the time might be stopped, thus causing a diff of 0. +					if ((time - start) == 0) +					{ +						m_LastSyncEnd = GetTimeSinceStartup (); +						return; +					} +					// Need to reset "start" here just in case we started to +					// busy wait and then the time was stopped while busy waiting +					start = time; +					i = 0; +				} +			} +			while(time - m_LastSyncEnd < frameTime); +			m_LastSyncEnd += frameTime; +			return; +		} +	} +	m_LastSyncEnd = GetTimeSinceStartup (); +} + +void TimeManager::Update () +{ +	AssertIf (m_UseFixedTimeStep); +	m_FrameCount++; +	m_RenderFrameCount++; +	if (m_SetTimeManually) +		return; + +	// Capture framerate is always constant +	if (m_CaptureFramerate > 0) +	{ +		#if DEBUG_TIME_MANAGER +		printf_console( "time: setting time using capture framerate of %i, timescale %f\n", m_CaptureFramerate, m_TimeScale ); +		#endif +		SetTime (m_DynamicTime.m_CurFrameTime + 1.0F / (float)m_CaptureFramerate * m_TimeScale); +		return; +	} + +	// Don't do anything to delta time the first frame! +	if (m_FirstFrameAfterReset) +	{ +		m_FirstFrameAfterReset = false; +		return; +	} + +	// When coming out of a pause / startup / level load we don't want to have a spike in delta time. +	// So just default to kStartupDeltaTime. +	if (m_FirstFrameAfterPause) +	{ +		m_FirstFrameAfterPause = false; +		#if DEBUG_TIME_MANAGER +		printf_console( "time: setting time first frame after pause\n" ); +		#endif +		SetTime (m_DynamicTime.m_CurFrameTime + kStartupDeltaTime * m_TimeScale); +		// This is not a real delta time so don't include in smoothed time +		m_ActiveTime.m_SmoothingWeight = 0.0f; +		m_DynamicTime.m_SmoothingWeight = 0.0f; +		return; +	} + +	double time = GetTimeSinceStartup () - m_ZeroTime; +	m_RealtimeStartOfFrame = time; + +	// clamp the delta time in case a frame takes too long. +	if (time - m_DynamicTime.m_CurFrameTime > m_MaximumTimestep) +	{ +		#if DEBUG_TIME_MANAGER +		printf_console( "time: maximum dt (was %f)\n", time - m_DynamicTime.m_CurFrameTime ); +		#endif +		SetTime (m_DynamicTime.m_CurFrameTime + m_MaximumTimestep * m_TimeScale); +		return; +	} + +	// clamp the delta time in case a frame goes to fast! (prevent delta time being zero) +	if (time - m_DynamicTime.m_CurFrameTime < kMinimumDeltaTime) +	{ +		#if DEBUG_TIME_MANAGER +		printf_console( "time: minimum dt (was %f)\n", time - m_DynamicTime.m_CurFrameTime ); +		#endif +		SetTime (m_DynamicTime.m_CurFrameTime + kMinimumDeltaTime * m_TimeScale); +		return; +	} + +	// Handle time scale +	if (!CompareApproximately (m_TimeScale, 1.0F)) +	{ +		#if DEBUG_TIME_MANAGER +		printf_console( "time: time scale path, delta %f\n", time - m_DynamicTime.m_CurFrameTime ); +		#endif +		float deltaTime = time - m_DynamicTime.m_CurFrameTime; +		SetTime (m_DynamicTime.m_CurFrameTime + deltaTime * m_TimeScale); +		return; +	} + +	#if DEBUG_TIME_MANAGER +	printf_console( "time: set to %f\n", time ); +	#endif +	m_DynamicTime.m_LastFrameTime = m_DynamicTime.m_CurFrameTime; +	m_DynamicTime.m_CurFrameTime = time; +	m_DynamicTime.m_DeltaTime = m_DynamicTime.m_CurFrameTime - m_DynamicTime.m_LastFrameTime; +	m_DynamicTime.m_InvDeltaTime = CalcInvDeltaTime(m_DynamicTime.m_DeltaTime); +	CalcSmoothDeltaTime (m_DynamicTime); + +	m_ActiveTime = m_DynamicTime; +} + +void TimeManager::SetDeltaTimeHack (float dt) +{ +	m_ActiveTime.m_DeltaTime = max(dt, kMinimumDeltaTime); +	m_ActiveTime.m_InvDeltaTime = CalcInvDeltaTime(m_ActiveTime.m_DeltaTime); +} + +void TimeManager::SetTime (double time) +{ +	AssertIf (m_UseFixedTimeStep); +//	AssertIf (time - m_DynamicTime.m_CurFrameTime < kMinimumDeltaTime * 0.1F); + +	m_DynamicTime.m_LastFrameTime = m_DynamicTime.m_CurFrameTime; +	m_DynamicTime.m_CurFrameTime = time; +	m_DynamicTime.m_DeltaTime = m_DynamicTime.m_CurFrameTime - m_DynamicTime.m_LastFrameTime; + +	m_DynamicTime.m_InvDeltaTime = CalcInvDeltaTime(m_DynamicTime.m_DeltaTime); +	CalcSmoothDeltaTime (m_DynamicTime); + +	m_ActiveTime = m_DynamicTime; + +	// Sync m_ZeroTime with timemanager time +	m_ZeroTime = GetTimeSinceStartup () - m_DynamicTime.m_CurFrameTime; +	#if DEBUG_TIME_MANAGER +	printf_console( "time: set to %f, sync zero to %f\n", time, m_ZeroTime ); +	#endif +} + +#if UNITY_EDITOR +void TimeManager::NextFrameEditor () { +	m_RenderFrameCount++; +} +#endif + +bool TimeManager::StepFixedTime () +{ +	if (m_FixedTime.m_CurFrameTime + m_FixedTime.m_DeltaTime > m_DynamicTime.m_CurFrameTime && !m_FirstFixedFrameAfterReset) +	{ +		m_ActiveTime = m_DynamicTime; +		m_UseFixedTimeStep = false; + +		return false; +	} + +	m_FixedTime.m_LastFrameTime = m_FixedTime.m_CurFrameTime; +	if (!m_FirstFixedFrameAfterReset) +		m_FixedTime.m_CurFrameTime += m_FixedTime.m_DeltaTime; + +	m_ActiveTime = m_FixedTime; +	m_UseFixedTimeStep = true; +	m_FirstFixedFrameAfterReset = false; + +	return true; +} + +void TimeManager::ResetTime () +{ +	AssertIf (m_UseFixedTimeStep); +	m_DynamicTime.m_CurFrameTime = 0.0F; +	m_DynamicTime.m_LastFrameTime = 0.0F; +	if (IsWorldPlaying()) +	{ +		m_DynamicTime.m_DeltaTime = 0.02F; +		m_DynamicTime.m_InvDeltaTime = 1.0F / m_DynamicTime.m_DeltaTime; +	} +	else +	{ +		m_DynamicTime.m_DeltaTime = 0.0F; +		m_DynamicTime.m_InvDeltaTime = 0.0F; +	} +	m_DynamicTime.m_SmoothDeltaTime = 0.0F; +	m_DynamicTime.m_SmoothingWeight = 0.0F; + +	m_FixedTime.m_CurFrameTime = 0.0F; +	m_FixedTime.m_LastFrameTime = 0.0F; +	// Dont erase the fixed delta time +	m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime; + +	m_ActiveTime = m_DynamicTime; + +	m_FirstFrameAfterReset = true; +	m_FirstFrameAfterPause = true; +	m_FirstFixedFrameAfterReset = true; + +	m_FrameCount = 0; +	m_RenderFrameCount = 0; +	m_ZeroTime = GetTimeSinceStartup (); +	m_RealZeroTime = m_ZeroTime; +	#if DEBUG_TIME_MANAGER +	printf_console( "time: startup, zero time %f\n", m_RealZeroTime ); +	#endif +	m_LevelLoadOffset = 0.0F; +	m_CaptureFramerate = 0; +	m_RealtimeStartOfFrame = 0.0; +} + +template<class TransferFunction> +void TimeManager::Transfer (TransferFunction& transfer) +{ +	Super::Transfer (transfer); +	transfer.Transfer (m_FixedTime.m_DeltaTime, "Fixed Timestep", kSimpleEditorMask); +	transfer.Transfer (m_MaximumTimestep, "Maximum Allowed Timestep", kSimpleEditorMask); +	transfer.Transfer (m_TimeScale, "m_TimeScale", kSimpleEditorMask); +} + +void TimeManager::SetFixedDeltaTime (float fixedStep) +{ +	fixedStep = clamp<float>(fixedStep, 0.0001F, 10.0F); +	m_FixedTime.m_DeltaTime = fixedStep; +	m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime; +	m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime; + +	SetMaximumDeltaTime(m_MaximumTimestep); +} + +void TimeManager::SetMaximumDeltaTime (float maxStep) +{ +	m_MaximumTimestep = max<float>(maxStep, m_FixedTime.m_DeltaTime); +} + +void TimeManager::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ +	Super::AwakeFromLoad(awakeMode); + +	m_FixedTime.m_InvDeltaTime = 1.0F / m_FixedTime.m_DeltaTime; +	m_FixedTime.m_SmoothDeltaTime = m_FixedTime.m_DeltaTime; +} + +void TimeManager::CheckConsistency () +{ +	Super::CheckConsistency (); + +	m_FixedTime.m_DeltaTime = clamp<float>(m_FixedTime.m_DeltaTime, 0.0001F, 10.0F); +	m_MaximumTimestep = max<float>(m_MaximumTimestep, m_FixedTime.m_DeltaTime); +} + +void TimeManager::DidFinishLoadingLevel () +{ +	m_LevelLoadOffset = -m_DynamicTime.m_CurFrameTime; +	// Trying to reconstruct what was intended here, this seems plausible: +	m_FirstFrameAfterPause = m_FirstFrameAfterReset = true; +} + +void TimeManager::SetTimeScale (float scale) { +	bool outOfRange = scale <= 100.0f && scale >= 0.0f; +	if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1)) +		outOfRange = scale < 100; +	 +	if (outOfRange) +	{ +		m_TimeScale = scale; +		SetDirty (); +	} +	else +	{ +		ErrorString ("Time.timeScale is out of range. Needs to be between 0 and 100."); +	} +} + +#if SUPPORT_REPRODUCE_LOG + +void TimeManager::ReadLog (std::ifstream& in) +{ +	if (!CheckReproduceTag("Time", in)) +	{ +		FailReproduction("Error reading reproduce log"); +		return; +	} + +	ReadFloat(in, m_DynamicTime.m_CurFrameTime); +	ReadFloat(in, m_DynamicTime.m_LastFrameTime); +	ReadFloat(in, m_DynamicTime.m_DeltaTime); +	ReadFloat(in, m_DynamicTime.m_SmoothDeltaTime); +	ReadFloat(in, m_DynamicTime.m_InvDeltaTime); +	ReadFloat(in, m_RealtimeStartOfFrame); + +	m_ActiveTime = m_DynamicTime; +} + +void TimeManager::WriteLog (std::ofstream& out) +{ +	// In the web player WebScripting.cpp injects the first new line for the frame +	#if !WEBPLUG +	out << std::endl; +	#endif + +	out << "Time" << std::endl; +	WriteFloat(out, m_DynamicTime.m_CurFrameTime); out << " "; +	WriteFloat(out, m_DynamicTime.m_LastFrameTime); out << " "; +	WriteFloat(out, m_DynamicTime.m_DeltaTime); out << " "; +	WriteFloat(out, m_DynamicTime.m_SmoothDeltaTime); out << " "; +	WriteFloat(out, m_DynamicTime.m_InvDeltaTime); out << " "; +	WriteFloat(out, m_RealtimeStartOfFrame); out << std::endl; + +	m_ActiveTime = m_DynamicTime; +} + +double TimeManager::GetRealtime() +{ +	#if SUPPORT_REPRODUCE_LOG + +	if (RunningReproduction() && (GetReproduceVersion() == 1 || GetReproduceVersion() == 2)) +	{ +		return m_RealtimeStartOfFrame; +	} + +	if (GetReproduceMode() == kPlaybackReproduceLog) +	{ +		std::ifstream& in = *GetReproduceInStream(); + +		if (!CheckReproduceTag("RealTime", in)) +		{ +			ErrorString("Grabbing realtime but there are no realtime calls recorded"); +			return m_RealtimeStartOfFrame; +		} + +		double realtime; +		ReadBigFloat(in, realtime); + +		return realtime; +	} +	else if (RunningReproduction()) +	{ +		double realtime = GetTimeSinceStartup() - m_RealZeroTime; + +		std::ofstream& out = *GetReproduceOutStream(); +		out << "RealTime "; +		WriteBigFloat(out, realtime); +		out << std::endl; + +		return realtime; +	} +	#endif +	double realtime = GetTimeSinceStartup() - m_RealZeroTime; +	return realtime; +} +#else +double TimeManager::GetRealtime() +{ +	return GetTimeSinceStartup() - m_RealZeroTime; +} +#endif + +#if ENABLE_CLUSTER_SYNC +template<class TransferFunc> +void TimeManager::ClusterTransfer (TransferFunc& transfer) +{ +    TRANSFER(m_DynamicTime.m_CurFrameTime); +    TRANSFER(m_DynamicTime.m_LastFrameTime); +    TRANSFER(m_DynamicTime.m_DeltaTime); +    TRANSFER(m_DynamicTime.m_SmoothDeltaTime); +    TRANSFER(m_DynamicTime.m_InvDeltaTime); +    TRANSFER(m_RealtimeStartOfFrame); +    TRANSFER(m_TimeScale); +} +#endif + + + +#if UNITY_ANDROID || UNITY_NACL +#include <sys/time.h> + +inline double clock_gettime_to_double () +{ +	timespec time; +	clock_gettime(CLOCK_MONOTONIC, &time); +	return time.tv_sec + (double)time.tv_nsec * 0.000000001; +} + +double TimeSinceStartupImpl () +{ +	static double sStartTime = 0; + +	if (sStartTime == 0) +		sStartTime = clock_gettime_to_double (); + +	return clock_gettime_to_double () - sStartTime; +} +#endif + + +IMPLEMENT_CLASS (TimeManager) +IMPLEMENT_OBJECT_SERIALIZE (TimeManager) +IMPLEMENT_CLUSTER_SERIALIZE(TimeManager) +GET_MANAGER (TimeManager) | 
