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 | |
7 | namespace 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 | |