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
13 inline Entity world_query_arg_id(World& world);
16 inline decltype(
auto) world_query_entity_arg_by_id(World& world, Entity entity, Entity
id);
19 inline decltype(
auto) world_query_entity_arg_by_id_raw(World& world, Entity entity, Entity
id);
21 inline void world_finish_write(World& world, Entity term, Entity entity);
24 static Entity observer_inherited_arg_id(World& world) {
25 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
26 if constexpr (std::is_same_v<Arg, Entity>)
29 return world_query_arg_id<Arg>(world);
33 static decltype(
auto) observer_inherited_entity_arg_by_id(World& world, Entity entity, Entity termId) {
34 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
35 if constexpr (std::is_same_v<Arg, Entity>)
37 else if constexpr (std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>)
38 return world_query_entity_arg_by_id_raw<T>(world, entity, termId);
40 return world_query_entity_arg_by_id<T>(world, entity, termId);
43 template <
typename... T,
typename Func,
size_t... I>
44 static void observer_invoke_inherited_args_by_id(
45 World& world, Entity entity,
const Entity* pArgIds, Func& func, std::index_sequence<I...>) {
46 func(observer_inherited_entity_arg_by_id<T>(world, entity, pArgIds[I])...);
50 GAIA_NODISCARD
static constexpr bool observer_is_write_arg() {
51 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
52 return std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>> &&
53 !std::is_same_v<Arg, Entity>;
56 template <
typename... T,
size_t... I>
58 observer_finish_args_by_id(World& world, Entity entity,
const Entity* pArgIds, std::index_sequence<I...>) {
59 Entity seenTerms[
sizeof...(T) > 0 ?
sizeof...(T) : 1]{};
61 const auto finish_term = [&](Entity term) {
63 if (seenTerms[i] == term)
67 seenTerms[seenCnt++] = term;
68 world_finish_write(world, term, entity);
73 if constexpr (observer_is_write_arg<T>())
74 finish_term(pArgIds[I]);
79 static void observer_finish_iter_writes(Iter& it) {
80 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
81 if (pChunk ==
nullptr)
84 for (
auto compIdx: it.touched_comp_indices())
85 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
87 auto terms = it.touched_terms();
91 auto& world = *it.world();
92 const auto entities = it.entity_rows();
94 const auto term = terms[i];
95 if (!world_is_out_of_line_component(world, term)) {
96 const auto compIdx = core::get_index(it.chunk()->ids_view(), term);
97 if (compIdx != BadIndex) {
98 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
103 GAIA_FOR_(entities.size(), j) {
104 world_finish_write(world, term, entities[j]);
109 template <
typename... T>
110 static bool observer_has_inherited_query_terms(Query& query, World& world, core::func_type_list<T...>) {
111 constexpr bool needsInheritedArgIds =
112 (!std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, Entity> || ... ||
false);
113 if constexpr (!needsInheritedArgIds)
116 auto& queryInfo = query.fetch();
117 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
125 template <
typename... T>
126 static void observer_init_inherited_arg_ids(Entity* pArgIds, World& world, core::func_type_list<T...>) {
127 if constexpr (
sizeof...(T) > 0) {
128 const Entity ids[] = {observer_inherited_arg_id<T>(world)...};
129 GAIA_FOR(
sizeof...(T)) pArgIds[i] = ids[i];
133 template <
typename Func,
typename... T>
134 static void observer_run_typed_on_entity(
135 ObserverRuntimeData& obs, World& world, Entity entity, Iter& it, Func& func, core::func_type_list<T...>,
136 bool hasInheritedTerms,
const Entity* pInheritedArgIds) {
137 constexpr bool needsInheritedArgIds =
138 (!std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, Entity> || ... ||
false);
139 if constexpr (!needsInheritedArgIds)
140 obs.query.run_query_on_chunk(it, func, core::func_type_list<T...>{});
142 if (hasInheritedTerms) {
143 observer_invoke_inherited_args_by_id<T...>(
144 world, entity, pInheritedArgIds, func, std::index_sequence_for<T...>{});
145 observer_finish_args_by_id<T...>(world, entity, pInheritedArgIds, std::index_sequence_for<T...>{});
149 obs.query.run_query_on_chunk(it, func, core::func_type_list<T...>{});
153 inline void ObserverRuntimeData::exec(Iter& iter, EntitySpan targets) {
154 const auto& queryInfo = query.fetch();
156 #if GAIA_PROFILER_CPU
157 const auto name = entity_name(*queryInfo.world(), entity);
158 const char* pScopeName = !name.empty() ? name.data() : sc_observer_query_func_str;
159 GAIA_PROF_SCOPE2(pScopeName);
162 auto* pWorld = iter.world();
163 const auto queryIdCnt = (uint32_t)plan.termCount;
164 const auto& termIds = queryTermIds;
166 const Archetype* pCachedArchetype =
nullptr;
167 uint8_t cachedIndices[ChunkHeader::MAX_COMPONENTS];
168 GAIA_FOR(ChunkHeader::MAX_COMPONENTS) {
169 cachedIndices[i] = 0xFF;
172 for (
auto e: targets) {
173 const auto& ec = pWorld->fetch(e);
174 if (pCachedArchetype != ec.pArchetype) {
175 pCachedArchetype = ec.pArchetype;
176 GAIA_FOR(ChunkHeader::MAX_COMPONENTS) {
177 cachedIndices[i] = 0xFF;
180 auto indicesView = queryInfo.try_indices_mapping_view(ec.pArchetype);
181 if (!indicesView.empty()) {
182 GAIA_FOR(queryIdCnt) {
183 cachedIndices[i] = indicesView[i];
186 GAIA_FOR(queryIdCnt) {
187 const auto queryId = termIds[i];
188 auto compIdx = world_component_index_comp_idx(*pWorld, *ec.pArchetype, queryId);
189 if (compIdx == BadIndex)
190 compIdx = core::get_index(ec.pArchetype->ids_view(), queryId);
191 cachedIndices[i] = (uint8_t)compIdx;
196 iter.set_archetype(ec.pArchetype);
197 iter.set_chunk(ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
198 iter.set_comp_indices(cachedIndices);
199 iter.set_term_ids(termIds.data());
200 iter.set_write_im(
false);
202 observer_finish_iter_writes(iter);
203 iter.clear_touched_writes();
207 class ObserverBuilder {
212 GAIA_ASSERT(m_world.valid(m_entity));
216 auto ss = m_world.acc_mut(m_entity);
217 auto& sys = ss.smut<Observer_>();
221 const Observer_& data()
const {
222 auto ss = m_world.acc(m_entity);
223 const auto& sys = ss.get<Observer_>();
227 ObserverRuntimeData& runtime_data() {
228 return m_world.observers().data(m_entity);
231 const ObserverRuntimeData& runtime_data()
const {
232 return m_world.observers().data(m_entity);
235 static void cache_term_id(ObserverRuntimeData& data, Entity term) {
236 GAIA_ASSERT(data.plan.termCount < MAX_ITEMS_IN_QUERY);
237 if (data.plan.termCount < MAX_ITEMS_IN_QUERY)
238 data.queryTermIds[data.plan.termCount] = term;
241 bool has_default_match_options(
const QueryTermOptions& options)
const {
244 return options.entSrc == EntityBad && options.entTrav == EntityBad;
247 bool is_complex_pair_term(Entity term)
const {
248 GAIA_ASSERT(term.pair());
252 if (is_wildcard(term))
254 if (term.id() == Is.id())
256 if (is_variable(term.id()) || is_variable(term.gen()))
262 bool is_fast_path_eligible_term(Entity term,
const QueryTermOptions& options)
const {
266 if (term == EntityBad)
269 if (!has_default_match_options(options))
279 if (is_complex_pair_term(term))
285 bool requires_diff_dispatch(Entity term,
const QueryTermOptions& options)
const {
286 if (options.entSrc != EntityBad || options.entTrav != EntityBad)
289 if (term == EntityBad || term == All)
292 if (is_variable((EntityId)term.id()))
296 if (is_wildcard(term))
298 if (is_variable((EntityId)term.id()) || is_variable((EntityId)term.gen()))
305 void register_diff_term(ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
306 if (!requires_diff_dispatch(term, options))
309 data.plan.diff.enabled =
true;
310 data.plan.refresh_exec_kind();
311 if (options.entTrav != EntityBad) {
312 bool hasRelation =
false;
313 GAIA_FOR(data.plan.diff.traversalRelationCount) {
314 if (data.plan.diff.traversalRelations[i] == options.entTrav) {
321 GAIA_ASSERT(data.plan.diff.traversalRelationCount < MAX_ITEMS_IN_QUERY);
322 if (data.plan.diff.traversalRelationCount < MAX_ITEMS_IN_QUERY)
323 data.plan.diff.traversalRelations[data.plan.diff.traversalRelationCount++] = options.entTrav;
326 update_diff_target_narrow_plan(data, op, term, options);
327 data.plan.refresh_exec_kind();
328 m_world.observers().add_diff_observer_term(m_world, m_entity, term, options);
331 void update_diff_target_narrow_plan(
332 ObserverRuntimeData& data, QueryOpKind op, Entity term,
const QueryTermOptions& options) {
333 using DispatchKind = ObserverPlan::DiffPlan::DispatchKind;
334 auto& diff = data.plan.diff;
335 if (diff.dispatchKind == DispatchKind::GlobalFallback)
338 const auto mark_unsupported = [&] {
339 diff.dispatchKind = DispatchKind::GlobalFallback;
340 diff.bindingVar = EntityBad;
341 diff.bindingRelation = EntityBad;
342 diff.traversalRelation = EntityBad;
343 diff.travKind = QueryTravKind::None;
344 diff.travDepth = QueryTermOptions::TravDepthUnlimited;
345 diff.traversalTriggerTermCount = 0;
348 if (options.entSrc != EntityBad || options.entTrav != EntityBad) {
349 if (options.entSrc == EntityBad || options.entTrav == EntityBad || op != QueryOpKind::All ||
350 !query_trav_has(options.travKind, QueryTravKind::Up) ||
351 query_trav_has(options.travKind, QueryTravKind::Down)) {
356 if (diff.bindingVar == EntityBad)
357 diff.bindingVar = options.entSrc;
358 else if (diff.bindingVar != options.entSrc) {
363 if (diff.traversalRelation == EntityBad) {
364 diff.traversalRelation = options.entTrav;
365 diff.travKind = options.travKind;
366 diff.travDepth = options.travDepth;
368 diff.traversalRelation != options.entTrav || diff.travKind != options.travKind ||
369 diff.travDepth != options.travDepth) {
374 bool hasTerm =
false;
375 GAIA_FOR(diff.traversalTriggerTermCount) {
376 if (diff.traversalTriggerTerms[i] == term) {
382 if (diff.traversalTriggerTermCount >= MAX_ITEMS_IN_QUERY) {
386 diff.traversalTriggerTerms[diff.traversalTriggerTermCount++] = term;
389 if (diff.dispatchKind == DispatchKind::LocalTargets)
390 diff.dispatchKind = DispatchKind::PropagatedTraversal;
394 if (term.pair() && op == QueryOpKind::All && !is_wildcard(term) && !is_variable((EntityId)term.id()) &&
395 is_variable((EntityId)term.gen())) {
396 const auto bindingVar = entity_from_id(m_world, term.gen());
397 const auto bindingRelation = entity_from_id(m_world, term.id());
398 if (!m_world.valid(bindingRelation)) {
403 if (diff.bindingVar == EntityBad)
404 diff.bindingVar = bindingVar;
405 else if (diff.bindingVar != bindingVar) {
410 if (diff.bindingRelation == EntityBad)
411 diff.bindingRelation = bindingRelation;
412 else if (diff.bindingRelation != bindingRelation) {
423 template <QueryOpKind Op,
typename T>
424 void reg_typed_term(ObserverRuntimeData& data) {
425 const auto term = m_world.template reg_comp<T>().entity;
426 cache_term_id(data, term);
427 data.plan.add_term_descriptor(Op, is_fast_path_eligible_term(term, QueryTermOptions{}));
428 register_diff_term(data, Op, term, QueryTermOptions{});
429 m_world.observers().add(m_world, term, m_entity, QueryMatchKind::Semantic);
432 template <QueryOpKind Op,
typename T>
433 void reg_typed_term(ObserverRuntimeData& data,
const QueryTermOptions& options) {
434 const auto term = m_world.template reg_comp<T>().entity;
435 cache_term_id(data, term);
436 data.plan.add_term_descriptor(Op, is_fast_path_eligible_term(term, options));
437 register_diff_term(data, Op, term, options);
438 m_world.observers().add(m_world, term, m_entity, options.matchKind);
442 ObserverBuilder(World& world, Entity entity): m_world(world), m_entity(entity) {}
446 ObserverBuilder& event(ObserverEvent event) {
448 data().event = event;
455 ObserverBuilder& kind(QueryCacheKind kind) {
457 runtime_data().query.kind(kind);
464 ObserverBuilder& scope(QueryCacheScope scope) {
466 runtime_data().query.scope(scope);
472 ObserverBuilder& add(QueryInput item) {
474 auto& data = runtime_data();
475 data.query.add(item);
477 QueryTermOptions options{};
478 options.entSrc = item.entSrc;
479 options.entTrav = item.entTrav;
480 options.travKind = item.travKind;
481 options.travDepth = item.travDepth;
482 options.access = item.access;
483 options.matchKind = item.matchKind;
485 cache_term_id(data, item.id);
486 data.plan.add_term_descriptor(item.op, is_fast_path_eligible_term(item.id, options));
487 register_diff_term(data, item.op, item.id, options);
488 m_world.observers().add(m_world, item.id, m_entity, item.matchKind);
494 ObserverBuilder& is(Entity entity,
const QueryTermOptions& options = {}) {
495 return all(Pair(Is, entity), options);
500 ObserverBuilder& in(Entity entity, QueryTermOptions options = {}) {
502 return all(Pair(Is, entity), options);
507 ObserverBuilder& all(Entity entity,
const QueryTermOptions& options = {}) {
509 auto& data = runtime_data();
510 data.query.all(entity, options);
511 cache_term_id(data, entity);
512 data.plan.add_term_descriptor(QueryOpKind::All, is_fast_path_eligible_term(entity, options));
513 register_diff_term(data, QueryOpKind::All, entity, options);
514 m_world.observers().add(m_world, entity, m_entity, options.matchKind);
518 ObserverBuilder& any(Entity entity,
const QueryTermOptions& options = {}) {
520 auto& data = runtime_data();
521 data.query.any(entity, options);
522 cache_term_id(data, entity);
523 data.plan.add_term_descriptor(QueryOpKind::Any, is_fast_path_eligible_term(entity, options));
524 register_diff_term(data, QueryOpKind::Any, entity, options);
525 m_world.observers().add(m_world, entity, m_entity, options.matchKind);
529 ObserverBuilder& or_(Entity entity,
const QueryTermOptions& options = {}) {
531 auto& data = runtime_data();
532 data.query.or_(entity, options);
533 cache_term_id(data, entity);
534 data.plan.add_term_descriptor(QueryOpKind::Or, is_fast_path_eligible_term(entity, options));
535 register_diff_term(data, QueryOpKind::Or, entity, options);
536 m_world.observers().add(m_world, entity, m_entity, options.matchKind);
540 ObserverBuilder& no(Entity entity,
const QueryTermOptions& options = {}) {
542 auto& data = runtime_data();
543 data.query.no(entity, options);
544 cache_term_id(data, entity);
545 data.plan.add_term_descriptor(QueryOpKind::Not, is_fast_path_eligible_term(entity, options));
546 register_diff_term(data, QueryOpKind::Not, entity, options);
547 m_world.observers().add(m_world, entity, m_entity, options.matchKind);
551 ObserverBuilder& match_prefab() {
553 runtime_data().query.match_prefab();
557 template <
typename T>
558 ObserverBuilder& all(
const QueryTermOptions& options) {
560 auto& data = runtime_data();
561 data.query.template all<T>(options);
562 reg_typed_term<QueryOpKind::All, T>(data, options);
566 template <
typename T>
567 ObserverBuilder& any(
const QueryTermOptions& options) {
569 auto& data = runtime_data();
570 data.query.template any<T>(options);
571 reg_typed_term<QueryOpKind::Any, T>(data, options);
575 template <
typename T>
576 ObserverBuilder& or_(
const QueryTermOptions& options) {
578 auto& data = runtime_data();
579 data.query.template or_<T>(options);
580 reg_typed_term<QueryOpKind::Or, T>(data, options);
584 template <
typename T>
585 ObserverBuilder& no(
const QueryTermOptions& options) {
587 auto& data = runtime_data();
588 data.query.template no<T>(options);
589 reg_typed_term<QueryOpKind::Not, T>(data, options);
595 template <
typename T>
596 ObserverBuilder& all() {
598 auto& data = runtime_data();
600 reg_typed_term<QueryOpKind::All, T>(data);
604 template <
typename T>
605 ObserverBuilder& any() {
607 auto& data = runtime_data();
609 reg_typed_term<QueryOpKind::Any, T>(data);
613 template <
typename T>
614 ObserverBuilder& or_() {
616 auto& data = runtime_data();
618 reg_typed_term<QueryOpKind::Or, T>(data);
622 template <
typename T>
623 ObserverBuilder& no() {
625 auto& data = runtime_data();
627 reg_typed_term<QueryOpKind::Not, T>(data);
635 ObserverBuilder& depth_order(Entity relation = ChildOf) {
637 runtime_data().query.depth_order(relation);
643 template <
typename Rel>
644 ObserverBuilder& depth_order() {
646 runtime_data().query.template depth_order<Rel>();
652 ObserverBuilder& name(
const char* name, uint32_t len = 0) {
653 m_world.name(m_entity, name, len);
657 ObserverBuilder& name_raw(
const char* name, uint32_t len = 0) {
658 m_world.name_raw(m_entity, name, len);
664 template <
typename Func>
665 ObserverBuilder& on_each(Func func) {
668 auto& ctx = runtime_data();
669 if constexpr (std::is_invocable_v<Func, Iter&>) {
670 ctx.on_each_func = [func](Iter& it) {
674 using InputArgs =
decltype(core::func_args(&Func::operator()));
676 const bool hasInheritedTerms = observer_has_inherited_query_terms(ctx.query, m_world, InputArgs{});
677 Entity inheritedArgIds[ChunkHeader::MAX_COMPONENTS] = {};
678 observer_init_inherited_arg_ids(inheritedArgIds, m_world, InputArgs{});
680 #if GAIA_ASSERT_ENABLED
684 auto& queryInfo = ctx.query.fetch();
685 ctx.query.match_all(queryInfo);
686 GAIA_ASSERT(ctx.query.unpack_args_into_query_has_all(queryInfo, InputArgs{}));
689 ctx.on_each_func = [e = m_entity, func, hasInheritedTerms, inheritedArgIds](Iter& it)
mutable {
690 auto& obs = it.world()->observers().data(e);
691 auto& world = *it.world();
692 const auto entity = it.view<Entity>()[0];
693 observer_run_typed_on_entity(obs, world, entity, it, func, InputArgs{}, hasInheritedTerms, inheritedArgIds);
697 return (ObserverBuilder&)*
this;
700 GAIA_NODISCARD Entity entity()
const {
704 void exec(Iter& iter, EntitySpan targets) {
705 auto& ctx = runtime_data();
706 ctx.exec(iter, targets);
static GAIA_NODISCARD bool uses_inherited_id_matching(const World &world, const QueryTerm &term)
Returns whether a term uses semantic inherited-id matching rather than direct storage matching.
Definition query.h:2645
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9