Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
observer_registry.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include "gaia/cnt/darray.h"
5#include "gaia/cnt/map.h"
6#include "gaia/cnt/set.h"
7#include "gaia/ecs/observer.h"
8
9#if GAIA_OBSERVERS_ENABLED
10namespace gaia {
11 namespace ecs {
12 class World;
13 class Archetype;
14
16 class ObserverRegistry {
17 struct DiffObserverIndex {
19 cnt::map<EntityLookupKey, cnt::darray<Entity>> direct;
21 cnt::map<EntityLookupKey, cnt::darray<Entity>> sourceTerm;
23 cnt::map<EntityLookupKey, cnt::darray<Entity>> traversalRelation;
25 cnt::map<EntityLookupKey, cnt::darray<Entity>> pairRelation;
27 cnt::map<EntityLookupKey, cnt::darray<Entity>> pairTarget;
29 cnt::darray<Entity> all;
31 cnt::darray<Entity> global;
32 };
33
34 struct PropagatedTargetCacheKey {
35 Entity bindingRelation = EntityBad;
36 Entity traversalRelation = EntityBad;
37 Entity rootTarget = EntityBad;
38 QueryTravKind travKind = QueryTravKind::None;
39 uint8_t travDepth = QueryTermOptions::TravDepthUnlimited;
40
41 GAIA_NODISCARD size_t hash() const {
42 size_t seed = EntityLookupKey(bindingRelation).hash();
43 seed ^= EntityLookupKey(traversalRelation).hash() + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
44 seed ^= EntityLookupKey(rootTarget).hash() + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
45 seed ^= size_t(travKind) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
46 seed ^= size_t(travDepth) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
47 return seed;
48 }
49
50 GAIA_NODISCARD bool operator==(const PropagatedTargetCacheKey& other) const {
51 return bindingRelation == other.bindingRelation && traversalRelation == other.traversalRelation &&
52 rootTarget == other.rootTarget && travKind == other.travKind && travDepth == other.travDepth;
53 }
54 };
55
56 struct PropagatedTargetCacheEntry {
57 uint32_t bindingRelationVersion = 0;
58 uint32_t traversalRelationVersion = 0;
59 cnt::darray<Entity> targets;
60 };
61
62 public:
63 struct DiffDispatcher {
64 struct Snapshot {
65 ObserverRuntimeData* pObs = nullptr;
66 uint32_t matchesBeforeIdx = UINT32_MAX;
67 };
68
69 struct MatchCacheEntry {
70 ObserverRuntimeData* pObsRepresentative = nullptr;
71 QueryInfo* pQueryInfoRepresentative = nullptr;
72 uint64_t queryHash = 0;
73 cnt::darray<Entity> matches;
74 };
75
76 struct TargetNarrowCacheEntry {
77 ObserverPlan::DiffPlan::DispatchKind kind = ObserverPlan::DiffPlan::DispatchKind::LocalTargets;
78 Entity bindingRelation = EntityBad;
79 Entity traversalRelation = EntityBad;
80 QueryTravKind travKind = QueryTravKind::None;
81 uint8_t travDepth = QueryTermOptions::TravDepthUnlimited;
82 QueryEntityArray triggerTerms{};
83 uint8_t triggerTermCount = 0;
84 cnt::darray<Entity> targets;
85 };
86
87 struct Context {
88 ObserverEvent event = ObserverEvent::OnAdd;
89 cnt::darray<Snapshot> observers;
90 cnt::darray<MatchCacheEntry> matchesBeforeCache;
91 cnt::darray<Entity> targets;
92 bool active = false;
93 bool targeted = false;
94 bool targetsAddedAfterPrepare = false;
95 bool resetTraversalCaches = false;
96 };
97
98 static void collect_query_matches(World& world, ObserverRuntimeData& obs, cnt::darray<Entity>& out);
99 static void collect_query_target_matches(
100 World& world, ObserverRuntimeData& obs, EntitySpan targets, cnt::darray<Entity>& out);
101 static void add_valid_targets(World& world, cnt::darray<Entity>& out, EntitySpan targets);
102 static void copy_target_narrow_plan(const ObserverRuntimeData& obs, TargetNarrowCacheEntry& entry);
103 GAIA_NODISCARD static bool
104 same_target_narrow_plan(const ObserverRuntimeData& obs, const TargetNarrowCacheEntry& entry);
105 static void normalize_targets(cnt::darray<Entity>& targets);
106 GAIA_NODISCARD static uint64_t query_hash(ObserverRuntimeData& obs);
107 GAIA_NODISCARD static bool same_query_ctx(const QueryCtx& left, const QueryCtx& right);
108 GAIA_NODISCARD static int32_t
109 find_match_cache_entry(cnt::darray<MatchCacheEntry>& cache, ObserverRuntimeData& obs);
110 GAIA_NODISCARD static Context prepare(
111 ObserverRegistry& registry, World& world, ObserverEvent event, EntitySpan terms,
112 EntitySpan targetEntities = {});
113 GAIA_NODISCARD static Context prepare_add_new(ObserverRegistry& registry, World& world, EntitySpan terms);
114 static void add_targets(World& world, Context& ctx, EntitySpan targets);
115 static void finish(World& world, Context&& ctx);
116 };
117
118 struct DirectDispatcher {
119 static void on_add(
120 ObserverRegistry& registry, World& world, const Archetype& archetype, EntitySpan entsAdded,
121 EntitySpan targets);
122 static void on_del(
123 ObserverRegistry& registry, World& world, const Archetype& archetype, EntitySpan entsRemoved,
124 EntitySpan targets);
125 static void on_set(ObserverRegistry& registry, World& world, Entity term, EntitySpan targets);
126 };
127
128 struct SharedDispatch {
129 template <bool DiffOnly, typename TObserverMap>
130 static void collect_from_map(
131 ObserverRegistry& registry, World& world, const TObserverMap& map, Entity term, uint64_t matchStamp);
132
133 static void collect_diff_from_list(
134 ObserverRegistry& registry, World& world, const cnt::darray<Entity>& observers, uint64_t matchStamp);
135
136 template <typename TObserverMap>
137 GAIA_NODISCARD static bool has_terms(const TObserverMap& map, EntitySpan terms) {
138 for (auto term: terms) {
139 const auto it = map.find(EntityLookupKey(term));
140 if (it != map.end() && !it->second.empty())
141 return true;
142 }
143
144 return false;
145 }
146
147 template <typename TObserverMap>
148 GAIA_NODISCARD static bool has_pair_relations(World& world, const TObserverMap& map, EntitySpan terms);
149
150 template <typename TObserverMap>
151 static void collect_for_event_term(
152 ObserverRegistry& registry, World& world, const TObserverMap& map, Entity term, uint64_t matchStamp);
153
154 template <typename TObserverMap>
155 static void collect_for_is_target(
156 ObserverRegistry& registry, World& world, const TObserverMap& map, Entity target, uint64_t matchStamp);
157
158 template <typename Func>
159 static void for_each_inherited_term(World& world, Entity baseEntity, Func&& func);
160
161 template <typename TObserverMap>
162 GAIA_NODISCARD static bool has_semantic_is_terms(World& world, const TObserverMap& map, EntitySpan terms);
163
164 template <typename TObserverMap>
165 GAIA_NODISCARD static bool has_inherited_terms(World& world, const TObserverMap& map, EntitySpan terms);
166
167 template <typename TObserverMap>
168 static void collect_for_inherited_terms(
169 ObserverRegistry& registry, World& world, const TObserverMap& map, Entity baseEntity, uint64_t matchStamp);
170
171 static void execute_targets(World& world, ObserverRuntimeData& obs, EntitySpan targets);
172 GAIA_NODISCARD static bool matches_direct_targets(
173 ObserverRuntimeData& obs, const Archetype& archetype, EntitySpan targets, QueryInfo* pQueryInfo = nullptr);
174 };
175
176 using DiffDispatchCtx = DiffDispatcher::Context;
177
178 private:
180 cnt::darray<ObserverRuntimeData*> m_relevant_observers_tmp;
182 cnt::map<EntityLookupKey, ObserverRuntimeData> m_observer_data;
184 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_add;
186 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_del;
188 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_set;
190 bool m_hasOnSetObservers = false;
192 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_add_is;
194 cnt::map<EntityLookupKey, cnt::darray<Entity>> m_observer_map_del_is;
196 DiffObserverIndex m_diff_index_add;
198 DiffObserverIndex m_diff_index_del;
200 cnt::map<PropagatedTargetCacheKey, PropagatedTargetCacheEntry> m_propagated_target_cache;
202 uint64_t m_current_match_stamp = 0;
203
204 GAIA_NODISCARD DiffObserverIndex& diff_index(ObserverEvent event) {
205 GAIA_ASSERT(event == ObserverEvent::OnAdd || event == ObserverEvent::OnDel);
206 return event == ObserverEvent::OnAdd ? m_diff_index_add : m_diff_index_del;
207 }
208
209 GAIA_NODISCARD const DiffObserverIndex& diff_index(ObserverEvent event) const {
210 GAIA_ASSERT(event == ObserverEvent::OnAdd || event == ObserverEvent::OnDel);
211 return event == ObserverEvent::OnAdd ? m_diff_index_add : m_diff_index_del;
212 }
213
214 GAIA_NODISCARD bool has_observers_for_term(Entity term) const {
215 const auto termKey = EntityLookupKey(term);
216 return observer_map_has_observers(m_observer_map_add, termKey) ||
217 observer_map_has_observers(m_observer_map_del, termKey) ||
218 observer_map_has_observers(m_observer_map_set, termKey);
219 }
220
221 GAIA_NODISCARD static bool can_mark_term_observed(World& world, Entity term);
222 GAIA_NODISCARD static bool is_semantic_is_term(Entity term, QueryMatchKind matchKind = QueryMatchKind::Semantic);
223 void mark_term_observed(World& world, Entity term, bool observed);
224
225 template <typename TObserverMap>
226 static void add_observer_to_map(TObserverMap& map, Entity term, Entity observer) {
227 const auto entityKey = EntityLookupKey(term);
228 const auto it = map.find(entityKey);
229 if (it == map.end())
230 map.emplace(entityKey, cnt::darray<Entity>{observer});
231 else
232 it->second.push_back(observer);
233 }
234
235 template <typename TObserverMap>
236 static void add_observer_to_map_unique(TObserverMap& map, Entity term, Entity observer) {
237 const auto entityKey = EntityLookupKey(term);
238 const auto it = map.find(entityKey);
239 if (it == map.end())
240 map.emplace(entityKey, cnt::darray<Entity>{observer});
241 else
242 add_observer_to_list(it->second, observer);
243 }
244
245 static void add_observer_to_list(cnt::darray<Entity>& list, Entity observer) {
246 if (core::has(list, observer))
247 return;
248 list.push_back(observer);
249 }
250
251 static void remove_observer_from_list(cnt::darray<Entity>& list, Entity observer) {
252 for (uint32_t i = 0; i < list.size();) {
253 if (list[i] == observer)
254 core::swap_erase_unsafe(list, i);
255 else
256 ++i;
257 }
258 }
259 template <typename TObserverMap>
260 GAIA_NODISCARD bool observer_map_has_observers(const TObserverMap& map, const EntityLookupKey& termKey) const {
261 const auto it = map.find(termKey);
262 if (it == map.end())
263 return false;
264
265 for (const auto observer: it->second) {
266 if (m_observer_data.find(EntityLookupKey(observer)) != m_observer_data.end())
267 return true;
268 }
269 return false;
270 }
271
272 static void collect_traversal_descendants(
273 World& world, Entity relation, Entity root, QueryTravKind travKind, uint8_t travDepth, uint64_t visitStamp,
274 cnt::darray<Entity>& outTargets);
275 static PropagatedTargetCacheEntry& ensure_propagated_targets_cached(
276 ObserverRegistry& registry, World& world, const ObserverRuntimeData& obs, Entity changedSource);
277 static void collect_propagated_targets_cached(
278 ObserverRegistry& registry, World& world, const ObserverRuntimeData& obs, Entity changedSource,
279 uint64_t visitStamp, cnt::set<EntityLookupKey>& visitedPairs, cnt::darray<Entity>& outTargets);
280 static void add_propagated_targets_cached(
281 ObserverRegistry& registry, World& world, const ObserverRuntimeData& obs, Entity changedSource,
282 cnt::darray<Entity>& outTargets);
283 GAIA_NODISCARD static bool collect_source_traversal_diff_targets(
284 ObserverRegistry& registry, World& world, ObserverRuntimeData& obs, EntitySpan changedTerms,
285 EntitySpan changedSources, cnt::darray<Entity>& outTargets);
286 GAIA_NODISCARD static bool collect_diff_targets_for_observer(
287 ObserverRegistry& registry, World& world, ObserverRuntimeData& obs, EntitySpan changedTerms,
288 EntitySpan changedTargets, cnt::darray<Entity>& outTargets);
289 GAIA_NODISCARD static bool
290 observer_uses_changed_traversal_relation(World& world, const ObserverRuntimeData& obs, EntitySpan changedTerms);
291
292 GAIA_NODISCARD static bool is_dynamic_pair_endpoint(EntityId endpoint) {
293 return is_wildcard(endpoint) || is_variable(endpoint);
294 }
295
296 GAIA_NODISCARD static bool is_observer_term_globally_dynamic(Entity term) {
297 if (term == EntityBad || term == All)
298 return true;
299
300 if (!term.pair())
301 return is_variable((EntityId)term.id());
302
303 const bool relDynamic = is_dynamic_pair_endpoint(term.id());
304 const bool tgtDynamic = is_dynamic_pair_endpoint(term.gen());
305 return relDynamic && tgtDynamic;
306 }
307
308 public:
309 GAIA_NODISCARD bool has_observers(Entity term) const {
310 return has_observers_for_term(term);
311 }
312
313 GAIA_NODISCARD bool has_on_set_observers(Entity term) const {
314 if (!m_hasOnSetObservers)
315 return false;
316 return m_observer_map_set.find(EntityLookupKey(term)) != m_observer_map_set.end();
317 }
318
319 void add_diff_observer_term(World& world, Entity observer, Entity term, const QueryTermOptions& options);
320 GAIA_NODISCARD DiffDispatchCtx
321 prepare_diff(World& world, ObserverEvent event, EntitySpan terms, EntitySpan targetEntities = {});
322 GAIA_NODISCARD DiffDispatchCtx prepare_diff_add_new(World& world, EntitySpan terms);
323 void add_diff_targets(World& world, DiffDispatchCtx& ctx, EntitySpan targets);
324 void finish_diff(World& world, DiffDispatchCtx&& ctx);
325
326 void teardown() {
327 for (auto& it: m_observer_data) {
328 auto& obs = it.second;
329 obs.on_each_func = {};
330 obs.query = {};
331 obs.plan = {};
332 obs.lastMatchStamp = 0;
333 }
334
335 m_relevant_observers_tmp = {};
336 m_observer_data = {};
337 m_observer_map_add = {};
338 m_observer_map_del = {};
339 m_observer_map_set = {};
340 m_hasOnSetObservers = false;
341 m_observer_map_add_is = {};
342 m_observer_map_del_is = {};
343 m_diff_index_add = {};
344 m_diff_index_del = {};
345 m_propagated_target_cache = {};
346 }
347
348 ObserverRuntimeData& data_add(Entity observer) {
349 return m_observer_data[EntityLookupKey(observer)];
350 }
351
352 GAIA_NODISCARD ObserverRuntimeData* data_try(Entity observer) {
353 const auto it = m_observer_data.find(EntityLookupKey(observer));
354 if (it == m_observer_data.end())
355 return nullptr;
356 return &it->second;
357 }
358
359 GAIA_NODISCARD const ObserverRuntimeData* data_try(Entity observer) const {
360 const auto it = m_observer_data.find(EntityLookupKey(observer));
361 if (it == m_observer_data.end())
362 return nullptr;
363 return &it->second;
364 }
365
366 GAIA_NODISCARD ObserverRuntimeData& data(Entity observer) {
367 auto* pData = data_try(observer);
368 GAIA_ASSERT(pData != nullptr);
369 return *pData;
370 }
371
372 GAIA_NODISCARD const ObserverRuntimeData& data(Entity observer) const {
373 const auto* pData = data_try(observer);
374 GAIA_ASSERT(pData != nullptr);
375 return *pData;
376 }
377
378 void try_mark_term_observed(World& world, Entity term);
379
385 void add(World& world, Entity term, Entity observer, QueryMatchKind matchKind = QueryMatchKind::Semantic);
386
390 void del(World& world, Entity term);
391
397 void on_add(World& world, const Archetype& archetype, EntitySpan entsAdded, EntitySpan targets);
398
404 void on_del(World& world, const Archetype& archetype, EntitySpan entsRemoved, EntitySpan targets);
405
410 void on_set(World& world, Entity term, EntitySpan targets);
411 };
412 } // namespace ecs
413} // namespace gaia
414#endif
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9