libecs-cpp 2.1.0
Entity Component System library in C++
Loading...
Searching...
No Matches
Timing.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <chrono>
5#include <cstdint>
6#include <stdexcept>
7#include <string>
8
9namespace ecs
10{
11 static_assert(sizeof(std::chrono::microseconds::rep) >= 8, "intervals need a 64-bit microsecond count");
12
14 constexpr std::chrono::microseconds MAX_INTERVAL = std::chrono::hours(24 * 36525);
15
17 constexpr std::chrono::microseconds DEFAULT_INTERVAL{1000000 / 30};
18
21 constexpr uint32_t MAX_CATCHUP = 2;
22
37 class Timing
38 {
39 public:
42 Timing(std::chrono::microseconds interval = DEFAULT_INTERVAL)
43 : updateInterval(Timing::checked(interval)),
44 lastUpdateTime(Timing::steadyNow())
45 {
46 }
47
50 [[deprecated("use Timing(std::chrono::microseconds) instead")]]
51 Timing(uint32_t frequency)
52 : updateInterval(static_cast<int64_t>(frequency)),
53 lastUpdateTime(Timing::steadyNow())
54 {
55 }
56
60 bool ShouldUpdate(std::chrono::microseconds now)
61 {
62 if(this->updateInterval == 0)
63 return true;
64
65 int64_t current_time = now.count();
66 int64_t elapsed = current_time - this->lastUpdateTime;
67 if(elapsed < 0)
68 elapsed = 0;
69
70 if(elapsed >= this->updateInterval)
71 {
72 // Remainder-carry: advance by the largest multiple of the interval that fits, so the
73 // average rate is exact and drift-free under jittered sampling.
74 int64_t intervals = elapsed / this->updateInterval;
75
76 // MAX_CATCHUP clamp: if stalled for too long, snap forward instead of catching up
77 // (prevents a burst after suspend or a long pause).
78 if(intervals > static_cast<int64_t>(MAX_CATCHUP))
79 this->lastUpdateTime = current_time;
80 else
81 this->lastUpdateTime += intervals * this->updateInterval;
82
83 return true;
84 }
85 return false;
86 }
87
90 {
91 return this->ShouldUpdate(std::chrono::microseconds(Timing::steadyNow()));
92 }
93
95 void Restart(std::chrono::microseconds now)
96 {
97 this->lastUpdateTime = now.count();
98 }
99
102 void SetInterval(std::chrono::microseconds interval)
103 {
104 this->updateInterval = Timing::checked(interval);
105 }
106
108 std::chrono::microseconds GetInterval() const
109 {
110 return std::chrono::microseconds(this->updateInterval);
111 }
112
115 [[deprecated("use SetInterval(std::chrono::microseconds) instead")]]
116 void SetFrequency(uint32_t frequency)
117 {
118 this->updateInterval = static_cast<int64_t>(frequency);
119 }
120
124 [[deprecated("use GetInterval() instead")]]
125 uint32_t GetFrequency() const
126 {
127 return static_cast<uint32_t>(std::min<int64_t>(this->updateInterval, UINT32_MAX));
128 }
129
130 private:
131 int64_t updateInterval;
132 int64_t lastUpdateTime;
133
134 static int64_t checked(std::chrono::microseconds interval)
135 {
136 if(interval.count() < 0 || interval > MAX_INTERVAL)
137 {
138 throw std::runtime_error("ecs::Timing: interval " + std::to_string(interval.count()) +
139 " us is outside 0 to " + std::to_string(MAX_INTERVAL.count()) + " us.");
140 }
141 return interval.count();
142 }
143
144 static int64_t steadyNow()
145 {
146 using namespace std::chrono;
147 return duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count();
148 }
149 };
150}
A repeating schedule: says when the next update is due.
Definition Timing.hpp:38
void SetFrequency(uint32_t frequency)
Changes the interval to a bare number of microseconds, without a range check.
Definition Timing.hpp:116
bool ShouldUpdate()
Says whether an update is due now by the real steady clock and, when it is, moves the schedule on.
Definition Timing.hpp:89
std::chrono::microseconds GetInterval() const
Returns the interval between updates.
Definition Timing.hpp:108
uint32_t GetFrequency() const
Returns the interval in microseconds, or 4 294 967 295 if it is longer than that number can hold.
Definition Timing.hpp:125
void Restart(std::chrono::microseconds now)
Starts a new schedule at the given instant: the first update is due one full interval later.
Definition Timing.hpp:95
Timing(std::chrono::microseconds interval=DEFAULT_INTERVAL)
Creates a schedule with the given interval, first due one full interval from now.
Definition Timing.hpp:42
Timing(uint32_t frequency)
Creates a schedule from a bare number, which is the interval in microseconds.
Definition Timing.hpp:51
void SetInterval(std::chrono::microseconds interval)
Changes the interval.
Definition Timing.hpp:102
bool ShouldUpdate(std::chrono::microseconds now)
Says whether an update is due at the given instant and, when it is, moves the schedule on.
Definition Timing.hpp:60
Definition Clock.hpp:10
constexpr std::chrono::microseconds MAX_INTERVAL
The longest interval or timer length that is accepted: 100 years of 365.25 days.
Definition Timing.hpp:14
constexpr uint32_t MAX_CATCHUP
Maximum number of intervals to catch up after a stall.
Definition Timing.hpp:21
constexpr std::chrono::microseconds DEFAULT_INTERVAL
The interval of a new schedule: 33 333 microseconds, about 30 updates a second.
Definition Timing.hpp:17