1#include "gaia/config/config.h"
5#include "gaia/ecs/chunk_iterator.h"
6#include "gaia/ecs/id.h"
7#include "gaia/ecs/observer.h"
9#if GAIA_OBSERVERS_ENABLED
12 inline void world_finish_write(World& world, Entity term, Entity entity);
14 static void observer_finish_iter_writes(Iter& it) {
15 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
16 if (pChunk ==
nullptr)
19 for (
auto compIdx: it.touched_comp_indices())
20 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
22 auto terms = it.touched_terms();
26 auto& world = *it.world();
27 const auto entities = it.entity_rows();
29 const auto term = terms[i];
30 if (!world_is_out_of_line_component(world, term)) {
31 const auto compIdx = core::get_index(it.chunk()->ids_view(), term);
33 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
38 GAIA_FOR_(entities.size(), j) {
39 world_finish_write(world, term, entities[j]);
44 inline void ObserverRuntimeData::exec(Iter& iter, EntitySpan targets) {
45 const auto& queryInfo = query.fetch();
48 const auto name = entity_name(*queryInfo.world(), entity);
49 const char* pScopeName = !name.empty() ? name.data() : sc_observer_query_func_str;
50 GAIA_PROF_SCOPE2(pScopeName);
53 auto* pWorld = iter.world();
54 const auto queryIdCnt = (uint32_t)plan.termCount;
55 const auto& termIds = queryTermIds;
57 const Archetype* pCachedArchetype =
nullptr;
60 cachedIndices[i] = 0xFF;
63 for (
auto e: targets) {
64 const auto& ec = pWorld->fetch(e);
65 if (pCachedArchetype != ec.pArchetype) {
66 pCachedArchetype = ec.pArchetype;
68 cachedIndices[i] = 0xFF;
71 auto indicesView = queryInfo.try_indices_mapping_view(ec.pArchetype);
72 if (!indicesView.empty()) {
73 GAIA_FOR(queryIdCnt) {
74 cachedIndices[i] = indicesView[i];
77 GAIA_FOR(queryIdCnt) {
78 const auto queryId = termIds[i];
79 auto compIdx = world_component_index_comp_idx(*pWorld, *ec.pArchetype, queryId);
81 compIdx = core::get_index(ec.pArchetype->ids_view(), queryId);
82 cachedIndices[i] = (uint8_t)compIdx;
87 iter.set_archetype(ec.pArchetype);
88 iter.set_chunk(ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
89 iter.set_comp_indices(cachedIndices);
90 iter.set_term_ids(termIds.data());
91 iter.set_write_im(
false);
93 observer_finish_iter_writes(iter);
94 iter.clear_touched_writes();
98 class ObserverBuilder {
103 GAIA_ASSERT(m_world.valid(m_entity));
107 auto ss = m_world.acc_mut(m_entity);
108 auto& sys = ss.smut<Observer_>();
112 const Observer_& data()
const {
113 auto ss = m_world.acc(m_entity);
114 const auto& sys = ss.get<Observer_>();
118 ObserverRuntimeData& runtime_data() {
119 return m_world.observers().data(m_entity);
122 const ObserverRuntimeData& runtime_data()
const {
123 return m_world.observers().data(m_entity);
126 static void cache_term_id(ObserverRuntimeData& data, Entity term) {
127 GAIA_ASSERT(data.plan.termCount < MAX_ITEMS_IN_QUERY);
128 if (data.plan.termCount < MAX_ITEMS_IN_QUERY)
129 data.queryTermIds[data.plan.termCount] = term;
132 bool has_default_match_options(
const QueryTermOptions& options)
const {
135 return options.entSrc == EntityBad && options.entTrav == EntityBad;
138 bool is_complex_pair_term(Entity term)
const {
139 GAIA_ASSERT(term.pair());
143 if (is_wildcard(term))
145 if (term.id() == Is.id())
147 if (is_variable(term.id()) || is_variable(term.gen()))
153 bool is_fast_path_eligible_term(Entity term,
const QueryTermOptions& options)
const {
157 if (term == EntityBad)
160 if (!has_default_match_options(options))
170 if (is_complex_pair_term(term))
176 bool requires_diff_dispatch(Entity term,
const QueryTermOptions& options)
const {
177 if (options.entSrc != EntityBad || options.entTrav != EntityBad)
180 if (term == EntityBad || term == All)
183 if (is_variable((EntityId)term.id()))
187 if (is_wildcard(term))
189 if (is_variable((EntityId)term.id()) || is_variable((EntityId)term.gen()))
196 void register_diff_term(ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
197 if (!requires_diff_dispatch(term, options))
200 data.plan.diff.enabled =
true;
201 data.plan.refresh_exec_kind();
202 if (options.entTrav != EntityBad) {
203 bool hasRelation =
false;
204 GAIA_FOR(data.plan.diff.traversalRelationCount) {
205 if (data.plan.diff.traversalRelations[i] == options.entTrav) {
212 GAIA_ASSERT(data.plan.diff.traversalRelationCount < MAX_ITEMS_IN_QUERY);
213 if (data.plan.diff.traversalRelationCount < MAX_ITEMS_IN_QUERY)
214 data.plan.diff.traversalRelations[data.plan.diff.traversalRelationCount++] = options.entTrav;
217 update_diff_target_narrow_plan(data, op, term, options);
218 data.plan.refresh_exec_kind();
219 m_world.observers().add_diff_observer_term(m_world, m_entity, term, options);
222 void update_diff_target_narrow_plan(
223 ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
224 using DispatchKind = ObserverPlan::DiffPlan::DispatchKind;
225 auto& diff = data.plan.diff;
226 if (diff.dispatchKind == DispatchKind::GlobalFallback)
229 const auto mark_unsupported = [&] {
230 diff.dispatchKind = DispatchKind::GlobalFallback;
231 diff.bindingVar = EntityBad;
232 diff.bindingRelation = EntityBad;
233 diff.traversalRelation = EntityBad;
234 diff.travKind = QueryTravKind::None;
235 diff.travDepth = QueryTermOptions::TravDepthUnlimited;
236 diff.traversalTriggerTermCount = 0;
239 if (options.entSrc != EntityBad || options.entTrav != EntityBad) {
240 if (options.entSrc == EntityBad || options.entTrav == EntityBad || op != QueryOpKind::All ||
241 !query_trav_has(options.travKind, QueryTravKind::Up) ||
242 query_trav_has(options.travKind, QueryTravKind::Down)) {
247 if (diff.bindingVar == EntityBad)
248 diff.bindingVar = options.entSrc;
249 else if (diff.bindingVar != options.entSrc) {
254 if (diff.traversalRelation == EntityBad) {
255 diff.traversalRelation = options.entTrav;
256 diff.travKind = options.travKind;
257 diff.travDepth = options.travDepth;
259 diff.traversalRelation != options.entTrav || diff.travKind != options.travKind ||
260 diff.travDepth != options.travDepth) {
265 bool hasTerm =
false;
266 GAIA_FOR(diff.traversalTriggerTermCount) {
267 if (diff.traversalTriggerTerms[i] == term) {
273 if (diff.traversalTriggerTermCount >= MAX_ITEMS_IN_QUERY) {
277 diff.traversalTriggerTerms[diff.traversalTriggerTermCount++] = term;
280 if (diff.dispatchKind == DispatchKind::LocalTargets)
281 diff.dispatchKind = DispatchKind::PropagatedTraversal;
285 if (term.pair() && op == QueryOpKind::All && !is_wildcard(term) && !is_variable((EntityId)term.id()) &&
286 is_variable((EntityId)term.gen())) {
287 const auto bindingVar = entity_from_id(m_world, term.gen());
288 const auto bindingRelation = entity_from_id(m_world, term.id());
289 if (!m_world.valid(bindingRelation)) {
294 if (diff.bindingVar == EntityBad)
295 diff.bindingVar = bindingVar;
296 else if (diff.bindingVar != bindingVar) {
301 if (diff.bindingRelation == EntityBad)
302 diff.bindingRelation = bindingRelation;
303 else if (diff.bindingRelation != bindingRelation) {
314 void reg_term(ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
315 cache_term_id(data, term);
316 data.plan.add_term_descriptor(op, is_fast_path_eligible_term(term, options));
317 register_diff_term(data, op, term, options);
318 m_world.observers().add(m_world, term, m_entity, options.matchKind);
322 ObserverBuilder(World& world, Entity entity): m_world(world), m_entity(entity) {}
326 ObserverBuilder& event(ObserverEvent event) {
328 data().event = event;
335 ObserverBuilder& kind(QueryCacheKind kind) {
337 runtime_data().query.kind(kind);
344 ObserverBuilder& scope(QueryCacheScope scope) {
346 runtime_data().query.scope(scope);
352 ObserverBuilder& add(QueryInput item) {
354 auto& data = runtime_data();
355 data.query.add(item);
357 QueryTermOptions options{};
358 options.entSrc = item.entSrc;
359 options.entTrav = item.entTrav;
360 options.travKind = item.travKind;
361 options.travDepth = item.travDepth;
362 options.access = item.access;
363 options.matchKind = item.matchKind;
365 cache_term_id(data, item.id);
366 data.plan.add_term_descriptor(item.op, is_fast_path_eligible_term(item.id, options));
367 register_diff_term(data, item.op, item.id, options);
368 m_world.observers().add(m_world, item.id, m_entity, item.matchKind);
374 ObserverBuilder& is(Entity entity,
const QueryTermOptions& options = {}) {
375 return all(Pair(Is, entity), options);
380 ObserverBuilder& in(Entity entity, QueryTermOptions options = {}) {
382 return all(Pair(Is, entity), options);
387 ObserverBuilder& all(Entity entity,
const QueryTermOptions& options = {}) {
389 auto& data = runtime_data();
390 data.query.all(entity, options);
391 reg_term(data, QueryOpKind::All, entity, options);
395 ObserverBuilder& any(Entity entity,
const QueryTermOptions& options = {}) {
397 auto& data = runtime_data();
398 data.query.any(entity, options);
399 reg_term(data, QueryOpKind::Any, entity, options);
403 ObserverBuilder& or_(Entity entity,
const QueryTermOptions& options = {}) {
405 auto& data = runtime_data();
406 data.query.or_(entity, options);
407 reg_term(data, QueryOpKind::Or, entity, options);
411 ObserverBuilder& no(Entity entity,
const QueryTermOptions& options = {}) {
413 auto& data = runtime_data();
414 data.query.no(entity, options);
415 reg_term(data, QueryOpKind::Not, entity, options);
419 ObserverBuilder& match_prefab() {
421 runtime_data().query.match_prefab();
425 template <
typename T>
426 ObserverBuilder& all(
const QueryTermOptions& options);
428 template <
typename T>
429 ObserverBuilder& any(
const QueryTermOptions& options);
431 template <
typename T>
432 ObserverBuilder& or_(
const QueryTermOptions& options);
434 template <
typename T>
435 ObserverBuilder& no(
const QueryTermOptions& options);
439 template <
typename T>
440 ObserverBuilder& all();
442 template <
typename T>
443 ObserverBuilder& any();
445 template <
typename T>
446 ObserverBuilder& or_();
448 template <
typename T>
449 ObserverBuilder& no();
455 ObserverBuilder& depth_order(Entity relation = ChildOf) {
457 runtime_data().query.depth_order(relation);
463 template <
typename Rel>
464 ObserverBuilder& depth_order();
468 ObserverBuilder& name(
const char* name, uint32_t len = 0) {
469 m_world.name(m_entity, name, len);
473 ObserverBuilder& name_raw(
const char* name, uint32_t len = 0) {
474 m_world.name_raw(m_entity, name, len);
480 template <
typename Func, std::enable_if_t<std::is_invocable_v<Func, Iter&>,
int> = 0>
481 ObserverBuilder& on_each(Func func) {
484 auto& ctx = runtime_data();
485 ctx.on_each_func = [func](Iter& it) {
489 return (ObserverBuilder&)*
this;
492 template <
typename Func, std::enable_if_t<!std::is_invocable_v<Func, Iter&>,
int> = 0>
493 ObserverBuilder& on_each(Func func);
495 GAIA_NODISCARD Entity entity()
const {
499 void exec(Iter& iter, EntitySpan targets) {
500 auto& ctx = runtime_data();
501 ctx.exec(iter, targets);
508 #include "gaia/ecs/observer_typed.inl"
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
constexpr uint32_t BadIndex
Sentinel index value returned by helpers when a lookup fails.
Definition utility.h:20