component.hxx 8.69 KB
Newer Older
1
2
3
4
5
6
7
8
#pragma once

#include <moodycamel/concurrentqueue.hpp>

#ifndef ECS_COMPONENT_INCLUDED
#include "component.hpp"
#endif

9
namespace mirrage::ecs {
10
11

	class Sparse_index_policy {
12
13
14
15
16
17
18
19
20
	  public:
		void attach(Entity_id, Component_index);
		void detach(Entity_id);
		void shrink_to_fit();
		auto find(Entity_id) const -> util::maybe<Component_index>;
		void clear();

	  private:
		std::unordered_map<Entity_id, Component_index> _table;
21
22
	};
	class Compact_index_policy {
23
24
25
26
27
28
29
30
31
32
	  public:
		Compact_index_policy();
		void attach(Entity_id, Component_index);
		void detach(Entity_id);
		void shrink_to_fit();
		auto find(Entity_id) const -> util::maybe<Component_index>;
		void clear();

	  private:
		std::vector<Component_index> _table;
33
34
35
	};


36
	template <class T>
37
	struct Pool_storage_policy_value_traits {
38
39
40
41
		static constexpr bool         supports_empty_values = true;
		static constexpr int_fast32_t max_free              = 8;
		using Marker_type                                   = Entity_handle;
		static constexpr Marker_type free_mark              = invalid_entity;
42
43
44
45
46

		static constexpr const Marker_type* marker_addr(const T* inst) {
			return T::component_base_t::marker_addr(inst);
		}
	};
47
	template <class T>
48
	constexpr bool Pool_storage_policy_value_traits<T>::supports_empty_values;
49
	template <class T>
50
	constexpr int_fast32_t Pool_storage_policy_value_traits<T>::max_free;
51
52
53
	template <class T>
	constexpr typename Pool_storage_policy_value_traits<T>::Marker_type
	        Pool_storage_policy_value_traits<T>::free_mark;
54
55


56
	template <std::size_t Chunk_size, class T>
57
	class Pool_storage_policy {
58
		using pool_t = util::pool<T, Chunk_size, Component_index, Pool_storage_policy_value_traits<T>>;
59

60
61
	  public:
		using iterator = typename pool_t::iterator;
62

63
64
65
66
		template <class... Args>
		auto emplace(Args&&... args) -> std::tuple<T&, Component_index> {
			return _pool.emplace_back(std::forward<Args>(args)...);
		}
67

68
		void replace(Component_index idx, T&& new_element) { _pool.replace(idx, std::move(new_element)); }
69

70
71
72
73
		template <typename F>
		void erase(Component_index idx, F&& relocate) {
			_pool.erase(idx, std::forward<F>(relocate));
		}
74

75
		void clear() { _pool.clear(); }
76

77
78
79
80
		template <typename F>
		void shrink_to_fit(F&& relocate) {
			_pool.shrink_to_fit(std::forward<F>(relocate));
		}
81

82
		auto get(Component_index idx) -> T& { return _pool.get(idx); }
83

84
85
86
87
88
89
90
		auto begin() noexcept -> iterator { return _pool.begin(); }
		auto end() noexcept -> iterator { return _pool.end(); }
		auto size() const -> Component_index { return _pool.size(); }
		auto empty() const -> bool { return _pool.empty(); }

	  private:
		pool_t _pool;
91
92
93
94
	};



95
	template <class T>
96
97
98
99
100
	class Component_container : public Component_container_base {
		friend class Entity_manager;
		friend void load(sf2::JsonDeserializer& s, Entity_handle& e);
		friend void save(sf2::JsonSerializer& s, const Entity_handle& e);

101
102
103
104
105
106
107
	  public:
		Component_container(Entity_manager& m) : _manager(m) {
			T::_validate_type_helper();
			_index.clear();
		}

		auto value_type() const noexcept -> Component_type override { return component_type_id<T>(); }
108

109
110
111
112
113
	  protected:
		void restore(Entity_handle owner, Deserializer& deserializer) override {
			auto entity_id = get_entity_id(owner, _manager);
			if(entity_id == invalid_entity_id) {
				FAIL("emplace_or_find_now of component from invalid/deleted entity");
114
115
			}

116
117
118
119
			auto& comp = [&]() -> T& {
				auto comp_idx = _index.find(entity_id);
				if(comp_idx.is_some()) {
					return _storage.get(comp_idx.get_or_throw());
120
121
				}

122
123
124
125
				auto comp = _storage.emplace(_manager, owner);
				_index.attach(entity_id, std::get<1>(comp));
				return std::get<0>(comp);
			}();
126

127
128
			load_component(deserializer, comp);
		}
129

130
131
132
133
134
135
136
		bool save(Entity_handle owner, Serializer& serializer) override {
			return find(owner).process(false, [&](T& comp) {
				serializer.write_value(T::name_save_as());
				save_component(serializer, comp);
				return true;
			});
		}
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
		void clear() override {
			_queued_deletions  = Queue<Entity_handle>{}; // clear by moving a new queue into the old
			_queued_insertions = Queue<Insertion>{};     // clear by moving a new queue into the old
			_index.clear();
			_storage.clear();
			_unoptimized_deletes = 0;
			_index.shrink_to_fit();
			_storage.shrink_to_fit([&](auto, auto& comp, auto new_idx) {
				_index.attach(comp.owner_handle().id(), new_idx);
			});
		}

		void process_queued_actions() override {
			process_deletions();
			process_insertions();
153

154
			if(_unoptimized_deletes > 32) {
155
156
157
158
159
160
				_unoptimized_deletes = 0;
				_index.shrink_to_fit();
				_storage.shrink_to_fit([&](auto, auto& comp, auto new_idx) {
					_index.attach(comp.owner_handle().id(), new_idx);
				});
			}
161
		}
162

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
		void process_deletions() {
			std::array<Entity_handle, 16> deletions_buffer;

			do {
				std::size_t deletions =
				        _queued_deletions.try_dequeue_bulk(deletions_buffer.data(), deletions_buffer.size());
				if(deletions > 0) {
					for(auto i = 0ull; i < deletions; i++) {
						auto entity_id = get_entity_id(deletions_buffer[i], _manager);
						if(entity_id == invalid_entity_id) {
							WARN("Discard delete of component " << T::name()
							                                    << " from invalid/deleted entity: "
							                                    << entity_name(deletions_buffer[i]));
							continue;
						}
178

179
180
181
182
						auto comp_idx_mb = _index.find(entity_id);
						if(!comp_idx_mb) {
							continue;
						}
183

184
185
						auto comp_idx = comp_idx_mb.get_or_throw();
						_index.detach(entity_id);
186

187
188
189
190
						Insertion insertion;
						if(_queued_insertions.try_dequeue(insertion)) {
							auto entity_id = get_entity_id(std::get<1>(insertion), _manager);
							if(entity_id == invalid_entity_id) {
191
								_storage.erase(comp_idx, [&](auto, auto& comp, auto new_idx) {
192
									_index.attach(comp.owner_handle().id(), new_idx);
193
								});
194
195
196
197

							} else {
								_storage.replace(comp_idx, std::move(std::get<0>(insertion)));
								_index.attach(std::get<1>(insertion).id(), comp_idx);
198
							}
199
200
201
202
203
204
						} else {
							_storage.erase(comp_idx, [&](auto, auto& comp, auto new_idx) {
								auto entity_id = get_entity_id(comp.owner_handle(), _manager);
								_index.attach(entity_id, new_idx);
							});
							_unoptimized_deletes++;
205
206
						}
					}
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
				} else {
					break;
				}
			} while(true);
		}
		void process_insertions() {
			std::array<Insertion, 8> insertions_buffer;

			do {
				std::size_t insertions = _queued_insertions.try_dequeue_bulk(insertions_buffer.data(),
				                                                             insertions_buffer.size());
				if(insertions > 0) {
					for(auto i = 0ull; i < insertions; i++) {
						auto entity_id = get_entity_id(std::get<1>(insertions_buffer[i]), _manager);
						if(entity_id == invalid_entity_id) {
							WARN("Discard insertion of component from invalid/deleted entity: "
							     << entity_name(std::get<1>(insertions_buffer[i])));
							continue;
225
						}
226
227
228

						auto comp = _storage.emplace(std::move(std::get<0>(insertions_buffer[i])));
						_index.attach(entity_id, std::get<1>(comp));
229
					}
230
231
232
233
234
				} else {
					break;
				}
			} while(true);
		}
235

236
237
238
239
240
241
242
243
244
245
246
247
	  public:
		template <typename F, typename... Args>
		void emplace(F&& init, Entity_handle owner, Args&&... args) {
			INVARIANT(owner != invalid_entity, "emplace on invalid entity");

			// construct T inplace inside the pair to avoid additional move
			auto inst = Insertion(std::piecewise_construct,
			                      std::forward_as_tuple(_manager, owner, std::forward<Args>(args)...),
			                      std::forward_as_tuple(owner));
			std::forward<F>(init)(inst.first);
			_queued_insertions.enqueue(std::move(inst));
		}
248

249
250
251
252
		void erase(Entity_handle owner) override {
			INVARIANT(owner, "erase on invalid entity");
			_queued_deletions.enqueue(owner);
		}
253

254
255
		auto find(Entity_handle owner) -> util::maybe<T&> {
			auto entity_id = get_entity_id(owner, _manager);
256

257
258
259
260
261
			return _index.find(entity_id).process(
			        util::maybe<T&>(), [&](auto comp_idx) { return util::justPtr(&_storage.get(comp_idx)); });
		}
		auto has(Entity_handle owner) const -> bool {
			auto entity_id = get_entity_id(owner, _manager);
262

263
264
			return _index.find(entity_id).is_some();
		}
265

266
267
268
269
		auto begin() noexcept { return _storage.begin(); }
		auto end() noexcept { return _storage.end(); }
		auto size() const noexcept { return _storage.size(); }
		auto empty() const noexcept { return _storage.empty(); }
270

271
		using iterator = typename T::storage_policy::iterator;
272

273
274
	  private:
		using Insertion = std::pair<T, Entity_handle>;
275

276
277
		template <class E>
		using Queue = moodycamel::ConcurrentQueue<E>;
278

279
280
		typename T::index_policy   _index;
		typename T::storage_policy _storage;
281

282
283
284
285
286
		Entity_manager&      _manager;
		Queue<Entity_handle> _queued_deletions;
		Queue<Insertion>     _queued_insertions;
		int                  _unoptimized_deletes = 0;
	};
287
}