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_component_uses_sparse_storage(world, term)) {
31 const auto compIdx = core::get_index(it.chunk()->ids_view(), term);
32 if (compIdx != BadIndex) {
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 #if GAIA_OBSERVERS_ENABLED && GAIA_ASSERT_ENABLED
55 pWorld->observer_callback_enter();
57 const auto queryIdCnt = (uint32_t)plan.termCount;
58 const auto& termIds = queryTermIds;
59 const auto terms = queryInfo.ctx().data.terms_view();
60 const QueryTerm* termsByField[MAX_ITEMS_IN_QUERY]{};
61 for (
const auto& term: terms)
62 termsByField[term.fieldIndex] = &term;
64 const Archetype* pCachedArchetype =
nullptr;
65 uint8_t cachedIndices[ChunkHeader::MAX_COMPONENTS];
66 GAIA_FOR(ChunkHeader::MAX_COMPONENTS) {
67 cachedIndices[i] = 0xFF;
70 for (
auto e: targets) {
71 const auto& ec = pWorld->fetch(e);
72 if (pCachedArchetype != ec.pArchetype) {
73 pCachedArchetype = ec.pArchetype;
74 GAIA_FOR(ChunkHeader::MAX_COMPONENTS) {
75 cachedIndices[i] = 0xFF;
78 auto indicesView = queryInfo.try_indices_mapping_view(ec.pArchetype);
79 if (!indicesView.empty()) {
80 GAIA_FOR(queryIdCnt) {
81 cachedIndices[i] = indicesView[i];
84 GAIA_FOR(queryIdCnt) {
85 const auto* pTerm = termsByField[i];
86 if (pTerm ==
nullptr || !query_term_maps_to_current_archetype(*pTerm))
89 const auto queryId = termIds[i];
90 auto compIdx = world_component_index_comp_idx(*pWorld, *ec.pArchetype, queryId);
91 if (compIdx == BadIndex)
92 compIdx = core::get_index(ec.pArchetype->ids_view(), queryId);
93 cachedIndices[i] = (uint8_t)compIdx;
98 iter.set_archetype(ec.pArchetype);
99 iter.set_chunk(ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
100 iter.set_comp_indices(cachedIndices);
101 iter.set_term_ids(termIds.data());
102 iter.set_write_im(
false);
104 observer_finish_iter_writes(iter);
105 iter.clear_touched_writes();
107 #if GAIA_OBSERVERS_ENABLED && GAIA_ASSERT_ENABLED
108 pWorld->observer_callback_leave();
112 class ObserverBuilder {
117 GAIA_ASSERT(m_world.valid(m_entity));
121 auto ss = m_world.acc_mut(m_entity);
122 auto& sys = ss.smut<Observer_>();
126 const Observer_& data()
const {
127 auto ss = m_world.acc(m_entity);
128 const auto& sys = ss.get<Observer_>();
132 ObserverRuntimeData& runtime_data() {
133 return m_world.observers().data(m_entity);
136 const ObserverRuntimeData& runtime_data()
const {
137 return m_world.observers().data(m_entity);
140 static void cache_term_id(ObserverRuntimeData& data, Entity term) {
141 GAIA_ASSERT(data.plan.termCount < MAX_ITEMS_IN_QUERY);
142 if (data.plan.termCount < MAX_ITEMS_IN_QUERY)
143 data.queryTermIds[data.plan.termCount] = term;
146 bool has_default_match_options(
const QueryTermOptions& options)
const {
149 return options.entSrc == EntityBad && options.entTrav == EntityBad;
152 bool is_complex_pair_term(Entity term)
const {
153 GAIA_ASSERT(term.pair());
157 if (is_wildcard(term))
159 if (term.id() == Is.
id())
161 if (is_variable(term.id()) || is_variable(term.gen()))
167 bool is_fast_path_eligible_term(Entity term,
const QueryTermOptions& options)
const {
171 if (term == EntityBad)
174 if (!has_default_match_options(options))
184 if (is_complex_pair_term(term))
190 bool requires_diff_dispatch(Entity term,
const QueryTermOptions& options)
const {
191 if (options.entSrc != EntityBad || options.entTrav != EntityBad)
194 if (term == EntityBad || term == All)
197 if (is_variable((EntityId)term.id()))
201 if (is_wildcard(term))
203 if (is_variable((EntityId)term.id()) || is_variable((EntityId)term.gen()))
210 void register_diff_term(ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
211 if (!requires_diff_dispatch(term, options))
214 data.plan.diff.enabled =
true;
215 data.plan.refresh_exec_kind();
216 if (options.entTrav != EntityBad) {
217 bool hasRelation =
false;
218 GAIA_FOR(data.plan.diff.traversalRelationCount) {
219 if (data.plan.diff.traversalRelations[i] == options.entTrav) {
226 GAIA_ASSERT(data.plan.diff.traversalRelationCount < MAX_ITEMS_IN_QUERY);
227 if (data.plan.diff.traversalRelationCount < MAX_ITEMS_IN_QUERY)
228 data.plan.diff.traversalRelations[data.plan.diff.traversalRelationCount++] = options.entTrav;
231 update_diff_target_narrow_plan(data, op, term, options);
232 data.plan.refresh_exec_kind();
233 m_world.observers().add_diff_observer_term(m_world, m_entity, term, options);
236 void update_diff_target_narrow_plan(
237 ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
238 using DispatchKind = ObserverPlan::DiffPlan::DispatchKind;
239 auto& diff = data.plan.diff;
240 if (diff.dispatchKind == DispatchKind::GlobalFallback)
243 const auto mark_unsupported = [&] {
244 diff.dispatchKind = DispatchKind::GlobalFallback;
245 diff.bindingVar = EntityBad;
246 diff.bindingRelation = EntityBad;
247 diff.traversalRelation = EntityBad;
248 diff.travKind = QueryTravKind::None;
249 diff.travDepth = QueryTermOptions::TravDepthUnlimited;
250 diff.traversalTriggerTermCount = 0;
253 if (options.entSrc != EntityBad || options.entTrav != EntityBad) {
254 if (options.entSrc == EntityBad || options.entTrav == EntityBad || op != QueryOpKind::All ||
255 !query_trav_has(options.travKind, QueryTravKind::Up) ||
256 query_trav_has(options.travKind, QueryTravKind::Down)) {
261 if (diff.bindingVar == EntityBad)
262 diff.bindingVar = options.entSrc;
263 else if (diff.bindingVar != options.entSrc) {
268 if (diff.traversalRelation == EntityBad) {
269 diff.traversalRelation = options.entTrav;
270 diff.travKind = options.travKind;
271 diff.travDepth = options.travDepth;
273 diff.traversalRelation != options.entTrav || diff.travKind != options.travKind ||
274 diff.travDepth != options.travDepth) {
279 bool hasTerm =
false;
280 GAIA_FOR(diff.traversalTriggerTermCount) {
281 if (diff.traversalTriggerTerms[i] == term) {
287 if (diff.traversalTriggerTermCount >= MAX_ITEMS_IN_QUERY) {
291 diff.traversalTriggerTerms[diff.traversalTriggerTermCount++] = term;
294 if (diff.dispatchKind == DispatchKind::LocalTargets)
295 diff.dispatchKind = DispatchKind::PropagatedTraversal;
299 if (term.pair() && op == QueryOpKind::All && !is_wildcard(term) && !is_variable((EntityId)term.id()) &&
300 is_variable((EntityId)term.gen())) {
301 const auto bindingVar = entity_from_id(m_world, term.gen());
302 const auto bindingRelation = entity_from_id(m_world, term.id());
303 if (!m_world.valid(bindingRelation)) {
308 if (diff.bindingVar == EntityBad)
309 diff.bindingVar = bindingVar;
310 else if (diff.bindingVar != bindingVar) {
315 if (diff.bindingRelation == EntityBad)
316 diff.bindingRelation = bindingRelation;
317 else if (diff.bindingRelation != bindingRelation) {
328 void reg_term(ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
329 cache_term_id(data, term);
330 data.plan.add_term_descriptor(op, is_fast_path_eligible_term(term, options));
331 register_diff_term(data, op, term, options);
332 m_world.observers().add(m_world, term, m_entity, options.matchKind);
336 ObserverBuilder(World& world, Entity entity): m_world(world), m_entity(entity) {}
340 ObserverBuilder& event(ObserverEvent event) {
342 data().event = event;
349 ObserverBuilder& kind(QueryCacheKind kind) {
351 runtime_data().query.kind(kind);
358 ObserverBuilder& scope(QueryCacheScope scope) {
360 runtime_data().query.scope(scope);
366 ObserverBuilder& add(QueryInput item) {
368 auto& data = runtime_data();
369 data.query.add(item);
371 QueryTermOptions options{};
372 options.entSrc = item.entSrc;
373 options.entTrav = item.entTrav;
374 options.travKind = item.travKind;
375 options.travDepth = item.travDepth;
376 options.access = item.access;
377 options.matchKind = item.matchKind;
379 cache_term_id(data, item.id);
380 data.plan.add_term_descriptor(item.op, is_fast_path_eligible_term(item.id, options));
381 register_diff_term(data, item.op, item.id, options);
382 m_world.observers().add(m_world, item.id, m_entity, item.matchKind);
388 ObserverBuilder& is(Entity entity,
const QueryTermOptions& options = {}) {
389 return all(Pair(Is, entity), options);
394 ObserverBuilder& in(Entity entity, QueryTermOptions options = {}) {
396 return all(Pair(Is, entity), options);
401 ObserverBuilder& all(Entity entity,
const QueryTermOptions& options = {}) {
403 auto& data = runtime_data();
404 data.query.all(entity, options);
405 reg_term(data, QueryOpKind::All, entity, options);
409 ObserverBuilder& any(Entity entity,
const QueryTermOptions& options = {}) {
411 auto& data = runtime_data();
412 data.query.any(entity, options);
413 reg_term(data, QueryOpKind::Any, entity, options);
417 ObserverBuilder& or_(Entity entity,
const QueryTermOptions& options = {}) {
419 auto& data = runtime_data();
420 data.query.or_(entity, options);
421 reg_term(data, QueryOpKind::Or, entity, options);
425 ObserverBuilder& no(Entity entity,
const QueryTermOptions& options = {}) {
427 auto& data = runtime_data();
428 data.query.no(entity, options);
429 reg_term(data, QueryOpKind::Not, entity, options);
433 ObserverBuilder& match_prefab() {
435 runtime_data().query.match_prefab();
439 template <
typename T>
440 ObserverBuilder& all(
const QueryTermOptions& options);
442 template <
typename T>
443 ObserverBuilder& any(
const QueryTermOptions& options);
445 template <
typename T>
446 ObserverBuilder& or_(
const QueryTermOptions& options);
448 template <
typename T>
449 ObserverBuilder& no(
const QueryTermOptions& options);
453 template <
typename T>
454 ObserverBuilder& all();
456 template <
typename T>
457 ObserverBuilder& any();
459 template <
typename T>
460 ObserverBuilder& or_();
462 template <
typename T>
463 ObserverBuilder& no();
469 ObserverBuilder& depth_order(Entity relation = ChildOf) {
471 runtime_data().query.depth_order(relation);
477 template <
typename Rel>
478 ObserverBuilder& depth_order();
482 ObserverBuilder& name(
const char* name, uint32_t len = 0) {
483 m_world.name(m_entity, name, len);
487 ObserverBuilder& name_raw(
const char* name, uint32_t len = 0) {
488 m_world.name_raw(m_entity, name, len);
494 template <
typename Func, std::enable_if_t<std::is_invocable_v<Func, Iter&>,
int> = 0>
495 ObserverBuilder& on_each(Func func) {
498 auto& ctx = runtime_data();
499 ctx.on_each_func = [func](Iter& it) {
503 return (ObserverBuilder&)*
this;
506 template <
typename Func, std::enable_if_t<!std::is_invocable_v<Func, Iter&>,
int> = 0>
507 ObserverBuilder& on_each(Func func);
509 GAIA_NODISCARD Entity entity()
const {
513 void exec(Iter& iter, EntitySpan targets) {
514 auto& ctx = runtime_data();
515 ctx.exec(iter, targets);
522 #include "gaia/ecs/observer_typed.inl"
GAIA_NODISCARD constexpr auto id() const noexcept
Entity index in the entity array.
Definition id.h:359