neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
system.hpp
Go to the documentation of this file.
1// system.hpp
2/*
3 * Copyright (c) 2018, 2020 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#pragma once
37
38#include <neolib/neolib.hpp>
39#include <atomic>
40#include <vector>
41#include <numeric>
42#include <chrono>
43#include <neolib/core/set.hpp>
51
52namespace neolib::ecs
53{
54 class i_ecs;
55
56 template <typename... ComponentData>
57 class system : public i_system
58 {
59 typedef system<ComponentData...> self_type;
60 private:
61 class thread : public async_task, public async_thread
62 {
63 public:
64 thread(self_type& aOwner) : async_task{ "neolib::ecs::system::thread" }, async_thread{ *this, "neolib::ecs::system::thread" }, iOwner{ aOwner }
65 {
66 start();
67 }
68 ~thread()
69 {
71 if (iOwner.waiting())
72 iOwner.signal();
73 }
74 public:
75 bool do_work(neolib::yield_type aYieldType = neolib::yield_type::NoYield) override
76 {
77 bool didWork = async_task::do_work(aYieldType);
78 if (iOwner.can_apply())
79 didWork = iOwner.apply() || didWork;
80 iOwner.yield();
81 if (iOwner.paused() && !iOwner.waiting())
82 iOwner.wait();
83 return didWork;
84 }
85 private:
86 self_type& iOwner;
87 };
89 struct performance_metrics
90 {
91 std::vector<std::chrono::microseconds> updateTimes;
92 std::size_t updateCounter = 0;
93 std::chrono::high_resolution_clock::time_point updateStartTime;
94 };
95 public:
96 struct no_thread : std::logic_error { no_thread() : std::logic_error{ "neolib::ecs::system::no_thread" } {} };
97 public:
98 system(i_ecs& aEcs) :
99 iEcs{ aEcs }, iComponents{ ComponentData::meta::id()... }, iPaused{ 0u }
100 {
101 (ecs().template component<ComponentData>(), ...);
102 }
103 system(const system& aOther) :
104 iEcs{ aOther.iEcs }, iComponents{ aOther.iComponents }, iPaused{ 0u }
105 {
106 (ecs().template component<ComponentData>(), ...);
107 }
108 system(system&& aOther) :
109 iEcs{ aOther.iEcs }, iComponents{ std::move(aOther.iComponents) }, iPaused{ 0u }
110 {
111 (ecs().template component<ComponentData>(), ...);
112 }
113 template <typename ComponentIdIter>
114 system(i_ecs& aEcs, ComponentIdIter aFirstComponent, ComponentIdIter aLastComponent) :
115 iEcs{ aEcs }, iComponents{ aFirstComponent, aLastComponent }, iPaused{ 0u }
116 {
117 (ecs().template component<ComponentData>(), ...);
118 if (ecs().all_systems_paused())
119 pause();
120 }
122 {
123 }
124 public:
125 i_ecs& ecs() const override
126 {
127 return iEcs;
128 }
129 public:
130 const i_set<component_id>& components() const override
131 {
132 return iComponents;
133 }
135 {
136 return iComponents;
137 }
138 public:
139 const i_component& component(component_id aComponentId) const override
140 {
141 return ecs().component(aComponentId);
142 }
143 i_component& component(component_id aComponentId) override
144 {
145 return ecs().component(aComponentId);
146 }
147 public:
148 bool can_apply() const override
149 {
150 return !paused() && (!have_thread() || (have_thread() && get_thread().in()));
151 }
152 bool paused() const override
153 {
154 return iPaused != 0u;
155 }
156 void pause() override
157 {
158 ++iPaused;
159 }
160 void resume() override
161 {
162 if (--iPaused == 0 && waiting())
163 signal();
164 }
165 void terminate() override
166 {
167 iThread = nullptr;
168 }
169 bool waiting() const override
170 {
171 return iWaiting;
172 }
173 void wait() override
174 {
175 if (!have_thread())
176 throw no_thread();
177 if (!get_thread().in())
178 throw wrong_thread();
179 if (!get_thread().is_alive())
180 return;
181 std::unique_lock<std::mutex> lock{ iMutex };
182 iWaiting = true;
183 iCondVar.wait(lock, [&]() { return !iWaiting; });
184 }
185 void wait_for(scalar aDuration) override
186 {
187 if (!have_thread())
188 throw no_thread();
189 if (!get_thread().in())
190 throw wrong_thread();
191 if (!get_thread().is_alive())
192 return;
193 std::unique_lock<std::mutex> lock{ iMutex };
194 iWaiting = true;
195 iCondVar.wait_for(lock, std::chrono::duration<double>(aDuration), [&](){ return !iWaiting; });
196 }
197 void signal() override
198 {
199 if (have_thread() && get_thread().in())
200 throw wrong_thread();
201 bool doIt = false;
202 {
203 std::unique_lock<std::mutex> lock{ iMutex };
204 if (iWaiting)
205 {
206 iWaiting = false;
207 doIt = true;
208 }
209 }
210 if (doIt)
211 iCondVar.notify_one();
212 }
213 public:
214 void start_thread_if() override
215 {
216 if (ecs().run_threaded(id()))
217 start_thread();
218 }
219 void start_thread() override
220 {
221 iThread = std::make_unique<thread>(*this);
222 }
223 public:
224 bool debug() const override
225 {
226 return iDebug;
227 }
228 void set_debug(bool aDebug) override
229 {
230 if (iDebug != aDebug)
231 {
232 iDebug = aDebug;
233 iPerformanceMetrics.clear();
234 }
235 }
236 std::chrono::microseconds update_time(std::size_t aMetricsIndex = 0) const override
237 {
238 if (iPerformanceMetrics.size() <= aMetricsIndex || iPerformanceMetrics[aMetricsIndex].updateTimes.empty())
239 return std::chrono::microseconds{ 0 };
240 return std::accumulate(iPerformanceMetrics[aMetricsIndex].updateTimes.begin(), iPerformanceMetrics[aMetricsIndex].updateTimes.end(), std::chrono::microseconds{}) / iPerformanceMetrics[aMetricsIndex].updateTimes.size();
241 }
242 protected:
243 bool have_thread() const
244 {
245 return iThread != nullptr;
246 }
247 thread& get_thread() const
248 {
249 if (have_thread())
250 return *iThread;
251 throw no_thread();
252 }
253 void yield(bool aSleep = false)
254 {
255 if (service<neolib::i_power>().green_mode_active() || aSleep)
256 neolib::thread::sleep(std::chrono::milliseconds{ 1 });
257 else
259 }
260 std::mutex& waiting_mutex()
261 {
262 return iMutex;
263 }
264 void start_update(std::size_t aMetricsIndex = 0)
265 {
266 if (debug())
267 {
268 if (iPerformanceMetrics.size() <= aMetricsIndex)
269 iPerformanceMetrics.resize(aMetricsIndex + 1);
270 iPerformanceMetrics[aMetricsIndex].updateStartTime = std::chrono::high_resolution_clock::now();
271 }
272 }
273 void end_update(std::size_t aMetricsIndex = 0)
274 {
275 if (debug())
276 {
277 if (iPerformanceMetrics.size() > aMetricsIndex)
278 {
279 std::size_t const updateQueueSize = 100;
280 auto const time = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - iPerformanceMetrics[aMetricsIndex].updateStartTime);
281 if (iPerformanceMetrics[aMetricsIndex].updateTimes.size() < updateQueueSize)
282 iPerformanceMetrics[aMetricsIndex].updateTimes.push_back(time);
283 else
284 {
285 iPerformanceMetrics[aMetricsIndex].updateTimes[iPerformanceMetrics[aMetricsIndex].updateCounter++] = time;
286 iPerformanceMetrics[aMetricsIndex].updateCounter %= updateQueueSize;
287 }
288 }
289 }
290 }
291 private:
292 i_ecs& iEcs;
293 component_list iComponents;
294 std::atomic<uint32_t> iPaused = 0u;
295 std::mutex iMutex;
296 std::condition_variable iCondVar;
297 std::atomic<bool> iWaiting = false;
298 std::atomic<bool> iDebug = false;
299 std::vector<performance_metrics> iPerformanceMetrics;
300 std::unique_ptr<thread> iThread;
301 };
302}
void set_destroying() override
i_thread & thread() const override
bool do_work(yield_type aYieldType=yield_type::NoYield) override
virtual const i_component & component(component_id aComponentId) const =0
virtual bool all_systems_paused() const =0
virtual const system_id & id() const =0
bool paused() const override
Definition system.hpp:152
void start_update(std::size_t aMetricsIndex=0)
Definition system.hpp:264
void wait_for(scalar aDuration) override
Definition system.hpp:185
void start_thread_if() override
Definition system.hpp:214
void terminate() override
Definition system.hpp:165
void wait() override
Definition system.hpp:173
void yield(bool aSleep=false)
Definition system.hpp:253
system(i_ecs &aEcs, ComponentIdIter aFirstComponent, ComponentIdIter aLastComponent)
Definition system.hpp:114
void pause() override
Definition system.hpp:156
i_ecs & ecs() const override
Definition system.hpp:125
bool can_apply() const override
Definition system.hpp:148
std::chrono::microseconds update_time(std::size_t aMetricsIndex=0) const override
Definition system.hpp:236
bool debug() const override
Definition system.hpp:224
void signal() override
Definition system.hpp:197
const i_set< component_id > & components() const override
Definition system.hpp:130
system(i_ecs &aEcs)
Definition system.hpp:98
void set_debug(bool aDebug) override
Definition system.hpp:228
i_set< component_id > & components() override
Definition system.hpp:134
system(system &&aOther)
Definition system.hpp:108
void resume() override
Definition system.hpp:160
i_component & component(component_id aComponentId) override
Definition system.hpp:143
thread & get_thread() const
Definition system.hpp:247
bool waiting() const override
Definition system.hpp:169
system(const system &aOther)
Definition system.hpp:103
const i_component & component(component_id aComponentId) const override
Definition system.hpp:139
std::mutex & waiting_mutex()
Definition system.hpp:260
void end_update(std::size_t aMetricsIndex=0)
Definition system.hpp:273
bool have_thread() const
Definition system.hpp:243
void start_thread() override
Definition system.hpp:219
static void sleep(const std::chrono::duration< double, std::milli > &aDuration)
static void yield()
double scalar
Definition numerical.hpp:63
bool is_alive(Object &aObject)
Definition i_object.hpp:55
boost::fast_pool_allocator< T, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex, NextSize > fast_pool_allocator
Definition plf_hive.h:79