1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Utility/BsTime.h"
4#include "Utility/BsTimer.h"
5#include "Math/BsMath.h"
6
7namespace bs
8{
9 constexpr UINT32 Time::MAX_ACCUM_FIXED_UPDATES;
10 constexpr UINT32 Time::NEW_FIXED_UPDATES_PER_FRAME;
11
12 const double Time::MICROSEC_TO_SEC = 1.0/1000000.0;
13
14 Time::Time()
15 {
16 mTimer = bs_new<Timer>();
17 mAppStartTime = mTimer->getStartMs();
18 mLastFrameTime = mTimer->getMicroseconds();
19 mAppStartUpDate = std::time(nullptr);
20 }
21
22 Time::~Time()
23 {
24 bs_delete(mTimer);
25 }
26
27 void Time::_update()
28 {
29 UINT64 currentFrameTime = mTimer->getMicroseconds();
30
31 if(!mFirstFrame)
32 mFrameDelta = (float)((currentFrameTime - mLastFrameTime) * MICROSEC_TO_SEC);
33 else
34 {
35 mFrameDelta = 0.0f;
36 mFirstFrame = false;
37 }
38
39 mTimeSinceStartMs = (UINT64)(currentFrameTime / 1000);
40 mTimeSinceStart = mTimeSinceStartMs / 1000.0f;
41
42 mLastFrameTime = currentFrameTime;
43
44 mCurrentFrame.fetch_add(1, std::memory_order_relaxed);
45 }
46
47 UINT32 Time::_getFixedUpdateStep(UINT64& step)
48 {
49 const UINT64 currentTime = getTimePrecise();
50
51 // Skip fixed update first frame (time delta is zero, and no input received yet)
52 if (mFirstFixedFrame)
53 {
54 mLastFixedUpdateTime = currentTime;
55 mFirstFixedFrame = false;
56 }
57
58 const UINT64 nextFrameTime = mLastFixedUpdateTime + mFixedStep;
59 if (nextFrameTime <= currentTime)
60 {
61 const INT64 simulationAmount = (INT64)std::max(currentTime - mLastFixedUpdateTime, mFixedStep); // At least one step
62 auto numIterations = (UINT32)Math::divideAndRoundUp(simulationAmount, (INT64)mFixedStep);
63
64 // Prevent physics from completely hogging the CPU. If the framerate is low, the physics will want to run many
65 // iterations per frame, slowing down the game even further. Therefore we limit the number of physics updates
66 // to a certain number (at the cost of simulation stability).
67
68 // However we don't use a fixed number per frame because performance spikes can cause some frames to take a very
69 // long time. These spikes can happen even in an otherwise well-performing application and will can wreak havoc
70 // on the physics simulation.
71
72 // Therefore we keep a "pool" which determines the number of physics frame iterations allowed to run. This pool
73 // gets exhausted with every iteration, and replenished with every new frame. The pool can hold a large number
74 // of frames which can then get used up during performance spikes, ensuring simulation stability. If the
75 // performance is consistently low (not just a spike), then the pool will get exhausted and physics updates
76 // will slow down to free up the CPU (at the cost of stability, but this time we have no other option).
77
78 auto stepus = (INT64)mFixedStep;
79 if (numIterations > mNumRemainingFixedUpdates)
80 {
81 stepus = Math::divideAndRoundUp(simulationAmount, (INT64)mNumRemainingFixedUpdates);
82 numIterations = (UINT32)Math::divideAndRoundUp(simulationAmount, (INT64)stepus);
83 }
84
85 assert(numIterations <= mNumRemainingFixedUpdates);
86
87 mNumRemainingFixedUpdates -= numIterations;
88 mNumRemainingFixedUpdates = std::min(MAX_ACCUM_FIXED_UPDATES, mNumRemainingFixedUpdates + NEW_FIXED_UPDATES_PER_FRAME);
89
90 step = stepus;
91 return numIterations;
92 }
93
94 step = 0;
95 return 0;
96 }
97
98 void Time::_advanceFixedUpdate(UINT64 step)
99 {
100 mLastFixedUpdateTime += step;
101 }
102
103 UINT64 Time::getTimePrecise() const
104 {
105 return mTimer->getMicroseconds();
106 }
107
108 String Time::getCurrentDateTimeString(bool isUTC)
109 {
110 std::time_t t = std::time(nullptr);
111 char out[100];
112 if (isUTC)
113 std::strftime(out, sizeof(out), "%A, %B %d, %Y %T", std::gmtime(&t));
114 else
115 std::strftime(out, sizeof(out), "%A, %B %d, %Y %T", std::localtime(&t));
116 return String(out);
117 }
118
119 String Time::getCurrentTimeString(bool isUTC)
120 {
121 std::time_t t = std::time(nullptr);
122 char out[15];
123 if (isUTC)
124 std::strftime(out, sizeof(out), "%T", std::gmtime(&t));
125 else
126 std::strftime(out, sizeof(out), "%T", std::localtime(&t));
127 return String(out);
128 }
129
130 String Time::getAppStartUpDateString(bool isUTC)
131 {
132 char out[100];
133 if (isUTC)
134 std::strftime(out, sizeof(out), "%A, %B %d, %Y %T", std::gmtime(&mAppStartUpDate));
135 else
136 std::strftime(out, sizeof(out), "%A, %B %d, %Y %T", std::localtime(&mAppStartUpDate));
137 return String(out);
138 }
139
140 Time& gTime()
141 {
142 return Time::instance();
143 }
144}
145