Plasma Engine  2.0
Loading...
Searching...
No Matches
IntervalScheduler_inl.h
1
2#include <Foundation/SimdMath/SimdRandom.h>
3
4PL_ALWAYS_INLINE plUInt32 plIntervalSchedulerBase::GetHistogramIndex(plTime value)
5{
6 if (value.IsZero())
7 return 0;
8
9 constexpr plUInt32 maxSlotIndex = HistogramSize - 1;
10 const double x = plMath::Max((value - m_MinInterval).GetSeconds() * m_fInvIntervalRange, 0.0);
11 const double i = plMath::Sqrt(x) * (maxSlotIndex - 1) + 1;
12 return plMath::Min(static_cast<plUInt32>(i), maxSlotIndex);
13}
14
15PL_ALWAYS_INLINE plTime plIntervalSchedulerBase::GetHistogramSlotValue(plUInt32 uiIndex)
16{
17 if (uiIndex == 0)
18 return plTime::MakeZero();
19
20 constexpr double norm = 1.0 / (HistogramSize - 2.0);
21 const double x = (uiIndex - 1) * norm;
22 return (x * x) * (m_MaxInterval - m_MinInterval) + m_MinInterval;
23}
24
25// static
26PL_ALWAYS_INLINE float plIntervalSchedulerBase::GetRandomZeroToOne(int pos, plUInt32& seed)
27{
29}
30
31constexpr plTime s_JitterRange = plTime::MakeFromMicroseconds(10);
32
33// static
34PL_ALWAYS_INLINE plTime plIntervalSchedulerBase::GetRandomTimeJitter(int pos, plUInt32& seed)
35{
36 const float x = plSimdRandom::FloatZeroToOne(plSimdVec4i(pos), plSimdVec4u(seed++)).x();
37 return s_JitterRange * (x * 2.0f - 1.0f);
38}
39
41
42template <typename T>
44{
45 return m_Interval.IsZeroOrPositive();
46}
47
48template <typename T>
50{
51 m_Interval = plTime::MakeFromSeconds(-1);
52}
53
55
56template <typename T>
57void plIntervalScheduler<T>::AddOrUpdateWork(const T& work, plTime interval)
58{
59 typename DataMap::Iterator it;
60 if (m_WorkIdToData.TryGetValue(work, it))
61 {
62 auto& data = it.Value();
63 plTime oldInterval = data.m_Interval;
64 if (interval == oldInterval)
65 return;
66
67 data.MarkAsInvalid();
68
69 const plUInt32 uiHistogramIndex = GetHistogramIndex(oldInterval);
70 m_Histogram[uiHistogramIndex]--;
71 }
72
73 Data data;
74 data.m_Work = work;
75 data.m_Interval = plMath::Max(interval, plTime::MakeZero());
76 data.m_DueTime = m_CurrentTime + GetRandomZeroToOne(m_Data.GetCount(), m_uiSeed) * data.m_Interval;
77 data.m_LastScheduledTime = m_CurrentTime;
78
79 m_WorkIdToData[work] = InsertData(data);
80
81 const plUInt32 uiHistogramIndex = GetHistogramIndex(data.m_Interval);
82 m_Histogram[uiHistogramIndex]++;
83}
85template <typename T>
86void plIntervalScheduler<T>::RemoveWork(const T& work)
87{
88 typename DataMap::Iterator it;
89 if (m_WorkIdToData.Remove(work, &it))
90 {
91 auto& data = it.Value();
92 plTime oldInterval = data.m_Interval;
93 data.MarkAsInvalid();
94
95 const plUInt32 uiHistogramIndex = GetHistogramIndex(oldInterval);
96 m_Histogram[uiHistogramIndex]--;
97 }
98}
99
100template <typename T>
102{
103 typename DataMap::Iterator it;
104 PL_VERIFY(m_WorkIdToData.TryGetValue(work, it), "Entry not found");
105 return it.Value().m_Interval;
106}
107
108template <typename T>
110{
111 if (deltaTime.IsZeroOrNegative())
112 return;
113
114 m_CurrentTime += deltaTime;
115
116 if (m_Data.IsEmpty())
117 return;
118
119 {
120 double fNumWork = 0;
121 for (plUInt32 i = 1; i < HistogramSize; ++i)
122 {
123 fNumWork += (1.0 / plMath::Max(m_HistogramSlotValues[i], deltaTime).GetSeconds()) * m_Histogram[i];
124 }
125 fNumWork *= deltaTime.GetSeconds();
126
127 const float fRemainder = static_cast<float>(plMath::Fraction(fNumWork));
128 const int pos = static_cast<int>(m_CurrentTime.GetNanoseconds());
129 const plUInt32 extra = GetRandomZeroToOne(pos, m_uiSeed) < fRemainder ? 1 : 0;
130 const plUInt32 uiScheduleCount = plMath::Min(static_cast<plUInt32>(fNumWork) + extra + m_Histogram[0], m_Data.GetCount());
131
132 // schedule work
133 {
134 auto RunWork = [&](typename DataMap::Iterator it, plUInt32 uiIndex) {
135 auto& data = it.Value();
136 if (data.IsValid())
137 {
138 if (runWorkCallback.IsValid())
139 {
140 runWorkCallback(data.m_Work, m_CurrentTime - data.m_LastScheduledTime);
141 }
142
143 // add a little bit of random jitter so we don't end up with perfect timings that might collide with other work
144 data.m_DueTime = m_CurrentTime + data.m_Interval + GetRandomTimeJitter(uiIndex, m_uiSeed);
145 data.m_LastScheduledTime = m_CurrentTime;
146 }
147
148 m_ScheduledWork.PushBack(it);
149 };
150
151 auto it = m_Data.GetIterator();
152 for (plUInt32 i = 0; i < uiScheduleCount; ++i, ++it)
153 {
154 RunWork(it, i);
155 }
156
157 // check if the next works have a zero interval if so execute them as well to fulfill the every frame guarantee
158 plUInt32 uiNumExtras = 0;
159 while (it.IsValid())
160 {
161 auto& data = it.Value();
162 if (data.m_Interval.IsPositive())
163 break;
164
165 RunWork(it, uiNumExtras);
166
167 ++uiNumExtras;
168 ++it;
169 }
170 }
171
172 // re-sort
173 for (auto& it : m_ScheduledWork)
174 {
175 if (it.Value().IsValid())
176 {
177 // make a copy of data and re-insert at new due time
178 Data data = it.Value();
179 m_WorkIdToData[data.m_Work] = InsertData(data);
180 }
181
182 m_Data.Remove(it);
183 }
184 m_ScheduledWork.Clear();
185 }
186}
187
188template <typename T>
190{
191 m_CurrentTime = plTime::MakeZero();
192 m_uiSeed = 0;
193 plMemoryUtils::ZeroFill(m_Histogram, HistogramSize);
194
195 m_Data.Clear();
196 m_WorkIdToData.Clear();
197}
198
199template <typename T>
201{
202 // make sure that we have a unique due time since the map can't store multiple keys with the same value
203 int pos = 0;
204 while (m_Data.Contains(data.m_DueTime))
205 {
206 data.m_DueTime += GetRandomTimeJitter(pos++, m_uiSeed);
207 }
208
209 return m_Data.Insert(data.m_DueTime, data);
210}
void PushBack(const T &value)
Pushes value at the end of the array.
Definition ArrayBase_inl.h:333
void Clear()
Clears the array.
Definition ArrayBase_inl.h:184
bool TryGetValue(const CompatibleKeyType &key, ValueType &out_value) const
Returns whether an entry with the given key was found and if found writes out the corresponding value...
void Clear()
Clears the table.
Definition HashTable_inl.h:355
bool Remove(const CompatibleKeyType &key, ValueType *out_pOldValue=nullptr)
Removes the entry with the given key. Returns whether an entry was removed and optionally writes out ...
Definition IntervalScheduler.h:65
void Update(plTime deltaTime, RunWorkCallback runWorkCallback)
Advances the scheduler by deltaTime and triggers runWorkCallback for each work that should be run dur...
Definition IntervalScheduler_inl.h:109
bool Contains(const CompatibleKeyType &key) const
Checks whether the given key is in the container.
void Clear()
Destroys all elements in the map and resets its size to zero.
Definition Map_inl.h:175
bool IsEmpty() const
Returns whether there are no elements in the map. O(1) operation.
Definition Map_inl.h:194
Iterator Insert(CompatibleKeyType &&key, CompatibleValueType &&value)
Inserts the key/value pair into the tree and returns an Iterator to it. O(log n) operation.
Definition Map_inl.h:535
bool Remove(const CompatibleKeyType &key)
Erases the key/value pair with the given key, if it exists. O(log n) operation.
Definition Map_inl.h:545
plUInt32 GetCount() const
Returns the number of elements currently stored in the map. O(1) operation.
Definition Map_inl.h:200
Iterator GetIterator()
Returns an Iterator to the very first element.
Definition Map_inl.h:207
static void ZeroFill(T *pDestination, size_t uiCount=1)
Zeros every byte in the provided memory buffer.
A SIMD 4-component vector class of signed 32b integers.
Definition SimdVec4i.h:9
A SIMD 4-component vector class of unsigned 32b integers.
Definition SimdVec4u.h:7
constexpr PL_ALWAYS_INLINE T Min(T f1, T f2)
Returns the smaller value, f1 or f2.
Definition Math_inl.h:27
PL_ALWAYS_INLINE Type Fraction(Type f)
Returns the fraction-part of f.
Definition Math_inl.h:305
PL_ALWAYS_INLINE double Sqrt(double f)
Returns the square root of f.
Definition MathDouble_inl.h:99
constexpr PL_ALWAYS_INLINE T Max(T f1, T f2)
Returns the greater value, f1 or f2.
Definition Math_inl.h:39
A generic delegate class which supports static functions and member functions.
Definition Delegate.h:76
PL_ALWAYS_INLINE bool IsValid() const
Checks whether this iterator points to a valid element.
Definition Map.h:27
Forward Iterator to iterate over all elements in sorted order.
Definition Map.h:103
PL_FORCE_INLINE ValueType & Value()
Returns the 'value' of the element that this iterator points to.
Definition Map.h:119
static plSimdVec4f FloatZeroToOne(const plSimdVec4i &vPosition, const plSimdVec4u &vSeed=plSimdVec4u::MakeZero())
Returns 4 random float values in range [0.0 ; 1.0], ie. including zero and one.
Definition SimdRandom_inl.h:24
The time class encapsulates a double value storing the time in seconds.
Definition Time.h:12
PL_ALWAYS_INLINE constexpr bool IsZeroOrNegative() const
Returns true if the stored time is zero or negative.
Definition Time.h:59
PL_ALWAYS_INLINE static constexpr plTime MakeFromSeconds(double fSeconds)
Creates an instance of plTime that was initialized from seconds.
Definition Time.h:30
PL_ALWAYS_INLINE static constexpr plTime MakeZero()
Creates an instance of plTime that was initialized with zero.
Definition Time.h:42
PL_ALWAYS_INLINE constexpr bool IsZero() const
Returns true if the stored time is exactly zero. That typically means the value was not changed from ...
Definition Time.h:50
constexpr double GetNanoseconds() const
Returns the nanoseconds value.
Definition Time_inl.h:15
PL_ALWAYS_INLINE constexpr bool IsZeroOrPositive() const
Returns true if the stored time is zero or positive.
Definition Time.h:62
constexpr double GetSeconds() const
Returns the seconds value.
Definition Time_inl.h:30
PL_ALWAYS_INLINE static constexpr plTime MakeFromMicroseconds(double fMicroseconds)
Creates an instance of plTime that was initialized from microseconds.
Definition Time.h:22