2#include "gaia/config/config.h"
4#include "gaia/cnt/darray.h"
5#include "gaia/cnt/map.h"
6#include "gaia/cnt/set.h"
7#include "gaia/ecs/observer.h"
9#if GAIA_OBSERVERS_ENABLED
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;
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;
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);
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;
56 struct PropagatedTargetCacheEntry {
57 uint32_t bindingRelationVersion = 0;
58 uint32_t traversalRelationVersion = 0;
59 cnt::darray<Entity> targets;
63 struct DiffDispatcher {
65 ObserverRuntimeData* pObs =
nullptr;
66 uint32_t matchesBeforeIdx = UINT32_MAX;
69 struct MatchCacheEntry {
70 ObserverRuntimeData* pObsRepresentative =
nullptr;
71 QueryInfo* pQueryInfoRepresentative =
nullptr;
72 uint64_t queryHash = 0;
73 cnt::darray<Entity> matches;
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;
88 ObserverEvent
event = ObserverEvent::OnAdd;
89 cnt::darray<Snapshot> observers;
90 cnt::darray<MatchCacheEntry> matchesBeforeCache;
91 cnt::darray<Entity> targets;
93 bool targeted =
false;
94 bool targetsAddedAfterPrepare =
false;
95 bool resetTraversalCaches =
false;
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);
118 struct DirectDispatcher {
120 ObserverRegistry& registry, World& world,
const Archetype& archetype, EntitySpan entsAdded,
123 ObserverRegistry& registry, World& world,
const Archetype& archetype, EntitySpan entsRemoved,
125 static void on_set(ObserverRegistry& registry, World& world, Entity term, EntitySpan targets);
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);
133 static void collect_diff_from_list(
134 ObserverRegistry& registry, World& world,
const cnt::darray<Entity>& observers, uint64_t matchStamp);
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())
147 template <
typename TObserverMap>
148 GAIA_NODISCARD
static bool has_pair_relations(World& world,
const TObserverMap& map, EntitySpan terms);
150 template <
typename TObserverMap>
151 static void collect_for_event_term(
152 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity term, uint64_t matchStamp);
154 template <
typename TObserverMap>
155 static void collect_for_is_target(
156 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity target, uint64_t matchStamp);
158 template <
typename Func>
159 static void for_each_inherited_term(World& world, Entity baseEntity, Func&& func);
161 template <
typename TObserverMap>
162 GAIA_NODISCARD
static bool has_semantic_is_terms(World& world,
const TObserverMap& map, EntitySpan terms);
164 template <
typename TObserverMap>
165 GAIA_NODISCARD
static bool has_inherited_terms(World& world,
const TObserverMap& map, EntitySpan terms);
167 template <
typename TObserverMap>
168 static void collect_for_inherited_terms(
169 ObserverRegistry& registry, World& world,
const TObserverMap& map, Entity baseEntity, uint64_t matchStamp);
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);
176 using DiffDispatchCtx = DiffDispatcher::Context;
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;
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;
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;
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);
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);
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);
230 map.emplace(entityKey, cnt::darray<Entity>{observer});
232 it->second.push_back(observer);
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);
240 map.emplace(entityKey, cnt::darray<Entity>{observer});
242 add_observer_to_list(it->second, observer);
245 static void add_observer_to_list(cnt::darray<Entity>& list, Entity observer) {
246 if (core::has(list, observer))
248 list.push_back(observer);
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);
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);
265 for (
const auto observer: it->second) {
266 if (m_observer_data.find(EntityLookupKey(observer)) != m_observer_data.end())
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);
292 GAIA_NODISCARD
static bool is_dynamic_pair_endpoint(EntityId endpoint) {
293 return is_wildcard(endpoint) || is_variable(endpoint);
296 GAIA_NODISCARD
static bool is_observer_term_globally_dynamic(Entity term) {
297 if (term == EntityBad || term == All)
301 return is_variable((EntityId)term.id());
303 const bool relDynamic = is_dynamic_pair_endpoint(term.id());
304 const bool tgtDynamic = is_dynamic_pair_endpoint(term.gen());
305 return relDynamic && tgtDynamic;
309 GAIA_NODISCARD
bool has_observers(Entity term)
const {
310 return has_observers_for_term(term);
313 GAIA_NODISCARD
bool has_on_set_observers(Entity term)
const {
314 if (!m_hasOnSetObservers)
316 return m_observer_map_set.find(EntityLookupKey(term)) != m_observer_map_set.end();
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);
327 for (
auto& it: m_observer_data) {
328 auto& obs = it.second;
329 obs.on_each_func = {};
332 obs.lastMatchStamp = 0;
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 = {};
348 ObserverRuntimeData& data_add(Entity observer) {
349 return m_observer_data[EntityLookupKey(observer)];
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())
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())
366 GAIA_NODISCARD ObserverRuntimeData& data(Entity observer) {
367 auto* pData = data_try(observer);
368 GAIA_ASSERT(pData !=
nullptr);
372 GAIA_NODISCARD
const ObserverRuntimeData& data(Entity observer)
const {
373 const auto* pData = data_try(observer);
374 GAIA_ASSERT(pData !=
nullptr);
378 void try_mark_term_observed(World& world, Entity term);
385 void add(World& world, Entity term, Entity observer, QueryMatchKind matchKind = QueryMatchKind::Semantic);
390 void del(World& world, Entity term);
397 void on_add(World& world,
const Archetype& archetype, EntitySpan entsAdded, EntitySpan targets);
404 void on_del(World& world,
const Archetype& archetype, EntitySpan entsRemoved, EntitySpan targets);
410 void on_set(World& world, Entity term, EntitySpan targets);
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9