Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
query_common.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <type_traits>
5
6#include "gaia/cnt/darray.h"
7#include "gaia/cnt/map.h"
8#include "gaia/cnt/sarray.h"
9#include "gaia/cnt/sarray_ext.h"
10#include "gaia/core/bit_utils.h"
11#include "gaia/core/hashing_policy.h"
12#include "gaia/core/span.h"
13#include "gaia/core/utility.h"
14#include "gaia/ecs/api.h"
15#include "gaia/ecs/component.h"
16#include "gaia/ecs/id.h"
17#include "gaia/ecs/query_fwd.h"
18#include "gaia/ecs/query_mask.h"
19
20namespace gaia {
21 namespace ecs {
22 class World;
23 class Archetype;
25 using InheritedTermDataView = std::span<const void* const>;
26 GAIA_NODISCARD uint32_t world_rel_version(const World& world, Entity relation);
27 GAIA_NODISCARD bool world_has_entity_term(const World& world, Entity entity, Entity term);
28 GAIA_NODISCARD bool world_has_entity_term_in(const World& world, Entity entity, Entity term);
29 GAIA_NODISCARD bool world_has_entity_term_direct(const World& world, Entity entity, Entity term);
30 GAIA_NODISCARD bool world_term_uses_inherit_policy(const World& world, Entity term);
31 GAIA_NODISCARD bool world_relation_uses_non_fragmenting_storage(const World& world, Entity relation);
32 GAIA_NODISCARD bool world_component_uses_sparse_storage(const World& world, Entity component);
33 GAIA_NODISCARD bool world_component_is_non_fragmenting(const World& world, Entity component);
34 GAIA_NODISCARD uint32_t world_count_direct_term_entities(const World& world, Entity term);
35 GAIA_NODISCARD uint32_t world_count_in_term_entities(const World& world, Entity term);
36 GAIA_NODISCARD uint32_t world_count_direct_term_entities_direct(const World& world, Entity term);
37 void world_collect_direct_term_entities(const World& world, Entity term, cnt::darray<Entity>& out);
38 void world_collect_in_term_entities(const World& world, Entity term, cnt::darray<Entity>& out);
39 void world_collect_direct_term_entities_direct(const World& world, Entity term, cnt::darray<Entity>& out);
40 GAIA_NODISCARD bool
41 world_each_direct_term_entity(const World& world, Entity term, void* ctx, bool (*func)(void*, Entity));
42 GAIA_NODISCARD bool
43 world_each_in_term_entity(const World& world, Entity term, void* ctx, bool (*func)(void*, Entity));
44 GAIA_NODISCARD bool
45 world_each_direct_term_entity_direct(const World& world, Entity term, void* ctx, bool (*func)(void*, Entity));
46 GAIA_NODISCARD bool world_entity_enabled(const World& world, Entity entity);
47 GAIA_NODISCARD bool world_entity_prefab(const World& world, Entity entity);
48 GAIA_NODISCARD const Archetype* world_entity_archetype(const World& world, Entity entity);
49 void world_finish_write(World& world, Entity term, Entity entity);
50 GAIA_NODISCARD uint32_t world_component_index_bucket_size(const World& world, Entity term);
51 GAIA_NODISCARD uint32_t world_component_index_comp_idx(const World& world, const Archetype& archetype, Entity term);
52 GAIA_NODISCARD uint32_t
53 world_component_index_match_count(const World& world, const Archetype& archetype, Entity term);
59 GAIA_NODISCARD GroupId group_by_func_depth_order(const World& world, const Archetype& archetype, Entity relation);
60 template <typename T>
61 GAIA_NODISCARD decltype(auto) world_direct_entity_arg(World& world, Entity entity);
62 template <typename T>
63 GAIA_NODISCARD decltype(auto) world_query_entity_arg(World& world, Entity entity);
64 template <typename T>
65 GAIA_NODISCARD Entity world_query_arg_id(World& world);
66 template <typename T>
67 GAIA_NODISCARD decltype(auto) world_query_entity_arg_by_id(World& world, Entity entity, Entity id);
68 template <typename T>
69 GAIA_NODISCARD decltype(auto) world_query_entity_arg_by_id_raw(World& world, Entity entity, Entity id);
71 GAIA_NODISCARD uint32_t world_entity_archetype_version(const World& world, Entity entity);
72
74 static constexpr uint32_t MAX_ITEMS_IN_QUERY = 12U;
76 static constexpr uint32_t MAX_TRAV_DEPTH = 128U;
77
78 GAIA_GCC_WARNING_PUSH()
79 // GCC is unnecessarily too strict about shadowing.
80 // We have a globally defined entity All and thinks QueryOpKind::All shadows it.
81 GAIA_GCC_WARNING_DISABLE("-Wshadow")
82
84 enum class QueryOpKind : uint8_t {
86 All,
88 Or,
90 Not,
92 Any,
94 Count
95 };
97 enum class QueryAccess : uint8_t {
99 None,
101 Read,
103 Write,
105 Match
106 };
108 enum class QueryInputFlags : uint8_t {
110 None,
112 Variable
113 };
115 enum class QueryTravKind : uint8_t {
117 None = 0x00,
119 Self = 0x01,
121 Up = 0x02,
123 Down = 0x04
124 };
125
126 GAIA_GCC_WARNING_POP()
127
128
132 GAIA_NODISCARD constexpr QueryTravKind operator|(QueryTravKind lhs, QueryTravKind rhs) {
133 return (QueryTravKind)((uint8_t)lhs | (uint8_t)rhs);
134 }
135
140 GAIA_NODISCARD constexpr bool query_trav_has(QueryTravKind value, QueryTravKind bit) {
141 return (((uint8_t)value) & ((uint8_t)bit)) != 0;
142 }
143
145 using QueryLookupHash = core::direct_hash_key<uint64_t>;
147 using QueryEntityArray = cnt::sarray<Entity, MAX_ITEMS_IN_QUERY>;
149 using QuerySerMap = cnt::map<QueryId, QuerySerBuffer>;
150
154 uint32_t index = 0;
156 uint32_t revision = 0;
157 };
158
163
165 static constexpr uint16_t ComponentIndexBad = (uint16_t)-1;
166
172 uint16_t compIdx = ComponentIndexBad;
174 uint16_t matchCount = 0;
175
179 GAIA_NODISCARD bool matches(const Archetype* pOther) const {
180 return pArchetype == pOther;
181 }
182 };
183
187 public:
191 using size_type = uint32_t;
192
193 private:
194 ComponentIndexEntry m_inline{};
196 size_type m_size = 0;
197
198 void collapse_to_inline_if_needed() {
199 if (m_size != 1 || m_items.empty())
200 return;
201
202 m_inline = m_items[0];
203 m_items.clear();
204 }
205
206 public:
207 ComponentIndexEntryArray() = default;
208 ComponentIndexEntryArray(ComponentIndexEntryArray&&) noexcept = default;
209 ComponentIndexEntryArray(const ComponentIndexEntryArray&) = default;
210 ComponentIndexEntryArray& operator=(ComponentIndexEntryArray&&) noexcept = default;
211 ComponentIndexEntryArray& operator=(const ComponentIndexEntryArray&) = default;
212
215 GAIA_NODISCARD bool empty() const noexcept {
216 return m_size == 0;
217 }
218
221 GAIA_NODISCARD size_type size() const noexcept {
222 return m_size;
223 }
224
227 GAIA_NODISCARD ComponentIndexEntry* data() noexcept {
228 return m_size <= 1 ? &m_inline : m_items.data();
229 }
230
233 GAIA_NODISCARD const ComponentIndexEntry* data() const noexcept {
234 return m_size <= 1 ? &m_inline : m_items.data();
235 }
236
239 GAIA_NODISCARD ComponentIndexEntry* begin() noexcept {
240 return data();
241 }
242
245 GAIA_NODISCARD const ComponentIndexEntry* begin() const noexcept {
246 return data();
247 }
248
251 GAIA_NODISCARD ComponentIndexEntry* end() noexcept {
252 return data() + m_size;
253 }
254
257 GAIA_NODISCARD const ComponentIndexEntry* end() const noexcept {
258 return data() + m_size;
259 }
260
264 GAIA_NODISCARD ComponentIndexEntry& operator[](size_type idx) noexcept {
265 GAIA_ASSERT(idx < m_size);
266 return data()[idx];
267 }
268
272 GAIA_NODISCARD const ComponentIndexEntry& operator[](size_type idx) const noexcept {
273 GAIA_ASSERT(idx < m_size);
274 return data()[idx];
275 }
276
279 GAIA_NODISCARD ComponentIndexEntry& back() noexcept {
280 GAIA_ASSERT(m_size > 0);
281 return (*this)[m_size - 1];
282 }
283
286 GAIA_NODISCARD const ComponentIndexEntry& back() const noexcept {
287 GAIA_ASSERT(m_size > 0);
288 return (*this)[m_size - 1];
289 }
290
294 if (m_size == 0) {
295 m_inline = entry;
296 m_size = 1;
297 return;
298 }
299
300 if (m_size == 1) {
301 m_items.push_back(m_inline);
302 m_items.push_back(entry);
303 m_size = 2;
304 return;
305 }
306
307 m_items.push_back(entry);
308 ++m_size;
309 }
310
312 void pop_back() {
313 GAIA_ASSERT(m_size > 0);
314 if (m_size == 1) {
315 m_size = 0;
316 return;
317 }
318
319 m_items.pop_back();
320 --m_size;
321 collapse_to_inline_if_needed();
322 }
323 };
324
328 EntityLookupKey key = EntityBadLookupKey;
331
335 GAIA_NODISCARD bool matches(EntityLookupKey other) const {
336 return key == other;
337 }
338 };
339
342
344 static constexpr QueryId QueryIdBad = (QueryId)-1;
346 static constexpr GroupId GroupIdMax = ((GroupId)-1) - 1;
347
349 struct QueryHandle {
351 static constexpr uint32_t IdMask = QueryIdBad;
352
353 private:
354 struct HandleData {
355 QueryId id;
356 uint32_t gen;
357 };
358
359 union {
360 HandleData data;
361 uint64_t val;
362 };
363
364 public:
365 constexpr QueryHandle() noexcept: val((uint64_t)-1) {}
366
370 QueryHandle(QueryId id, uint32_t gen) {
371 data.id = id;
372 data.gen = gen;
373 }
374 ~QueryHandle() = default;
375
376 QueryHandle(QueryHandle&&) noexcept = default;
377 QueryHandle(const QueryHandle&) = default;
378 QueryHandle& operator=(QueryHandle&&) noexcept = default;
379 QueryHandle& operator=(const QueryHandle&) = default;
380
384 GAIA_NODISCARD constexpr bool operator==(const QueryHandle& other) const noexcept {
385 return val == other.val;
386 }
390 GAIA_NODISCARD constexpr bool operator!=(const QueryHandle& other) const noexcept {
391 return val != other.val;
392 }
393
396 GAIA_NODISCARD auto id() const {
397 return data.id;
398 }
401 GAIA_NODISCARD auto gen() const {
402 return data.gen;
403 }
406 GAIA_NODISCARD auto value() const {
407 return val;
408 }
409 };
410
412 inline static const QueryHandle QueryHandleBad = QueryHandle();
413
418
419 private:
421 QueryHandle m_handle;
423 LookupHash m_hash;
424
425 static LookupHash calc(QueryHandle handle) {
426 return {core::calculate_hash64(handle.value())};
427 }
428
429 public:
431 static constexpr bool IsDirectHashKey = true;
432
433 QueryHandleLookupKey() = default;
436 explicit QueryHandleLookupKey(QueryHandle handle): m_handle(handle), m_hash(calc(handle)) {}
437 ~QueryHandleLookupKey() = default;
438
441 QueryHandleLookupKey& operator=(const QueryHandleLookupKey&) = default;
442 QueryHandleLookupKey& operator=(QueryHandleLookupKey&&) = default;
443
447 return m_handle;
448 }
449
452 size_t hash() const {
453 return (size_t)m_hash.hash;
454 }
455
459 bool operator==(const QueryHandleLookupKey& other) const {
460 if GAIA_LIKELY (m_hash != other.m_hash)
461 return false;
462
463 return m_handle == other.m_handle;
464 }
465
469 bool operator!=(const QueryHandleLookupKey& other) const {
470 return !operator==(other);
471 }
472 };
473
475 inline static const QueryHandleLookupKey QueryHandleBadLookupKey = QueryHandleLookupKey(QueryHandleBad);
476
478 struct QueryInput {
480 static constexpr uint8_t TravDepthUnlimited = 0;
481
483 QueryOpKind op = QueryOpKind::All;
485 QueryAccess access = QueryAccess::Read;
491 Entity entSrc = EntityBad;
494 Entity entTrav = EntityBad;
498 QueryTravKind travKind = QueryTravKind::Self | QueryTravKind::Up;
503 QueryMatchKind matchKind = QueryMatchKind::Semantic;
504 };
505
512
514 Entity entSrc = EntityBad;
516 Entity entTrav = EntityBad;
518 QueryTravKind travKind = QueryTravKind::Self | QueryTravKind::Up;
524 QueryAccess access = QueryAccess::None;
526 QueryMatchKind matchKind = QueryMatchKind::Semantic;
527
532 entSrc = source;
533 return *this;
534 }
535
539 QueryTermOptions& trav(Entity relation = ChildOf) {
540 entTrav = relation;
541 travKind = QueryTravKind::Self | QueryTravKind::Up;
543 return *this;
544 }
545
549 QueryTermOptions& trav_up(Entity relation = ChildOf) {
550 entTrav = relation;
551 travKind = QueryTravKind::Up;
553 return *this;
554 }
555
559 QueryTermOptions& trav_parent(Entity relation = ChildOf) {
560 entTrav = relation;
561 travKind = QueryTravKind::Up;
562 travDepth = 1;
563 return *this;
564 }
565
570 entTrav = relation;
571 travKind = QueryTravKind::Self | QueryTravKind::Up;
572 travDepth = 1;
573 return *this;
574 }
575
579 QueryTermOptions& trav_down(Entity relation = ChildOf) {
580 entTrav = relation;
581 travKind = QueryTravKind::Down;
583 return *this;
584 }
585
590 entTrav = relation;
591 travKind = QueryTravKind::Self | QueryTravKind::Down;
593 return *this;
594 }
595
599 QueryTermOptions& trav_child(Entity relation = ChildOf) {
600 entTrav = relation;
601 travKind = QueryTravKind::Down;
602 travDepth = 1;
603 return *this;
604 }
605
610 entTrav = relation;
611 travKind = QueryTravKind::Self | QueryTravKind::Down;
612 travDepth = 1;
613 return *this;
614 }
615
619 QueryTermOptions& trav_kind(QueryTravKind kind) {
620 travKind = kind;
621 return *this;
622 }
623
627 QueryTermOptions& trav_depth(uint8_t maxDepth) {
628 travDepth = maxDepth;
629 return *this;
630 }
631
635 access = QueryAccess::Match;
636 return *this;
637 }
638
642 access = QueryAccess::Read;
643 return *this;
644 }
645
649 access = QueryAccess::Write;
650 return *this;
651 }
652
657 matchKind = QueryMatchKind::Direct;
658 return *this;
659 }
660
665 matchKind = QueryMatchKind::In;
666 return *this;
667 }
668 };
669
683 uint8_t readCnt = 0;
685 uint8_t writeCnt = 0;
687 bool hasPairPatterns = false;
688
691 GAIA_NODISCARD std::span<const Entity> reads_view() const {
692 return {reads.data(), readCnt};
693 }
694
697 GAIA_NODISCARD std::span<const Entity> writes_view() const {
698 return {writes.data(), writeCnt};
699 }
700
703 void add_read(Entity entity) {
704 if (entity == EntityBad || core::has(reads_view(), entity))
705 return;
706
707 GAIA_ASSERT(readCnt < MAX_ITEMS_IN_QUERY);
708 if (readCnt < MAX_ITEMS_IN_QUERY) {
709 reads[readCnt++] = entity;
710 hasPairPatterns |= entity.pair() && (is_wildcard(entity) || is_variable((EntityId)entity.id()) ||
711 is_variable((EntityId)entity.gen()));
712 }
713 }
714
717 void add_write(Entity entity) {
718 if (entity == EntityBad || core::has(writes_view(), entity))
719 return;
720
721 GAIA_ASSERT(writeCnt < MAX_ITEMS_IN_QUERY);
722 if (writeCnt < MAX_ITEMS_IN_QUERY) {
723 writes[writeCnt++] = entity;
724 hasPairPatterns |= entity.pair() && (is_wildcard(entity) || is_variable((EntityId)entity.id()) ||
725 is_variable((EntityId)entity.gen()));
726 }
727 }
728
732 GAIA_NODISCARD QueryAccess access(Entity entity) const {
733 if (core::has(writes_view(), entity))
734 return QueryAccess::Write;
735 if (core::has(reads_view(), entity))
736 return QueryAccess::Read;
737 return QueryAccess::None;
738 }
739 };
740
742 struct QueryTerm {
750 QueryTravKind travKind;
752 uint8_t travDepth;
754 QueryMatchKind matchKind;
758 QueryOpKind op;
760 uint8_t fieldIndex = 0;
762 QueryAccess access = QueryAccess::None;
763
767 bool operator==(const QueryTerm& other) const {
768 return id == other.id && src == other.src && entTrav == other.entTrav && travKind == other.travKind &&
769 travDepth == other.travDepth && matchKind == other.matchKind && op == other.op;
770 }
774 bool operator!=(const QueryTerm& other) const {
775 return !operator==(other);
776 }
777 };
778
783 constexpr bool query_term_less_for_lookup(const QueryTerm& lhs, const QueryTerm& rhs) {
784 if (lhs.op != rhs.op)
785 return lhs.op < rhs.op;
786
787 if (lhs.id != rhs.id)
788 return SortComponentCond()(lhs.id, rhs.id);
789
790 if (lhs.src != rhs.src)
791 return SortComponentCond()(lhs.src, rhs.src);
792
793 if (lhs.entTrav != rhs.entTrav)
794 return SortComponentCond()(lhs.entTrav, rhs.entTrav);
795
796 if (lhs.travKind != rhs.travKind)
797 return (uint8_t)lhs.travKind < (uint8_t)rhs.travKind;
798
799 if (lhs.travDepth != rhs.travDepth)
800 return lhs.travDepth < rhs.travDepth;
801
802 return (uint8_t)lhs.matchKind < (uint8_t)rhs.matchKind;
803 }
804
807 inline void canonicalize_lookup_terms(std::span<QueryTerm> terms) {
808 const auto idsCnt = (uint32_t)terms.size();
809 if (idsCnt > 0) {
810 uint32_t orCnt = 0;
811 uint32_t orIdx = BadIndex;
812 GAIA_FOR(idsCnt) {
813 if (terms[i].op != QueryOpKind::Or)
814 continue;
815 ++orCnt;
816 orIdx = i;
817 if (orCnt > 1)
818 break;
819 }
820
821 if (orCnt == 1)
822 terms[orIdx].op = QueryOpKind::All;
823 }
824
825 core::sort(terms.data(), terms.data() + idsCnt, [](const QueryTerm& left, const QueryTerm& right) {
826 return query_term_less_for_lookup(left, right);
827 });
828 }
829
832 inline void canonicalize_lookup_changed(std::span<Entity> changed) {
833 const auto changedCnt = (uint32_t)changed.size();
834 if (changedCnt > 1)
835 core::sort(changed.data(), changed.data() + changedCnt, SortComponentCond{});
836 }
837
841 GAIA_NODISCARD inline bool term_has_variables(const QueryTerm& term) {
842 if (is_variable(term.src))
843 return true;
844
845 if (term.id.pair())
846 return is_variable(EntityId(term.id.id())) || is_variable(EntityId(term.id.gen()));
847
848 return is_variable(EntityId(term.id.id()));
849 }
850
854 GAIA_NODISCARD inline bool query_term_maps_to_current_archetype(const QueryTerm& term) {
855 return term.src == EntityBad && term.entTrav == EntityBad && !term_has_variables(term);
856 }
857
862 GAIA_NODISCARD inline bool query_term_uses_potential_inherited_id_matching(const QueryTerm& term) {
863 const auto id = term.id;
864 return term.matchKind == QueryMatchKind::Semantic && term.src == EntityBad && term.entTrav == EntityBad &&
865 !term_has_variables(term) && !is_wildcard(id) && !is_variable((EntityId)id.id()) &&
866 (!id.pair() || !is_variable((EntityId)id.gen()));
867 }
868
870 using QueryTermArray = cnt::sarray_ext<QueryTerm, MAX_ITEMS_IN_QUERY>;
872 using QueryTermSpan = std::span<QueryTerm>;
874 using QueryRemappingArray = cnt::sarray_ext<uint8_t, MAX_ITEMS_IN_QUERY>;
875
881 QueryId serId = QueryIdBad;
882
886 GAIA_NODISCARD QuerySerBuffer& ser_buffer(World* world) {
887 return query_buffer(*world, serId);
888 }
891 void ser_buffer_reset(World* world) {
892 query_buffer_reset(*world, serId);
893 }
894 };
895
897 struct QueryCtx {
899 const World* w{};
901 ComponentCache* cc{};
906
908 enum QueryFlags : uint16_t {
910 Empty = 0x00,
916 Complex = 0x04,
918 Recompile = 0x08,
928 OrderGroups = 0x100,
933 };
934
936 enum class CachePolicy : uint8_t {
938 Immediate,
940 Lazy,
942 Dynamic,
943 };
944
946 enum class CreateArchetypeMatchKind : uint8_t {
948 Vm,
951 };
952
954 enum class DynamicCacheKind : uint8_t {
956 None,
964 Variable,
966 Mixed
967 };
968
992
1022
1024 struct Data {
1038 uint8_t exclusionCnt = 0;
1040 uint8_t relationCnt = 0;
1042 uint8_t sourceEntityCnt = 0;
1044 uint8_t sourceTermCnt = 0;
1047
1049 void clear() {
1051 exclusionCnt = 0;
1052 relationCnt = 0;
1053 sourceEntityCnt = 0;
1054 sourceTermCnt = 0;
1056 }
1057
1060 GAIA_NODISCARD std::span<const Entity> create_selectors_view() const {
1062 }
1063
1066 GAIA_NODISCARD std::span<const Entity> exclusions_view() const {
1067 return {exclusions.data(), exclusionCnt};
1068 }
1069
1072 GAIA_NODISCARD std::span<const Entity> relations_view() const {
1073 return {relations.data(), relationCnt};
1074 }
1075
1078 GAIA_NODISCARD std::span<const Entity> src_entities_view() const {
1080 }
1081
1085 flags = (DependencyFlags)(flags | dependency);
1086 }
1087
1091 GAIA_NODISCARD bool has_dep_flag(DependencyFlags dependency) const {
1092 return (flags & dependency) != 0;
1093 }
1094
1097 void add_rel(Entity relation) {
1098 if (relation == EntityBad || core::has(relations_view(), relation))
1099 return;
1100
1101 GAIA_ASSERT(relationCnt < MAX_ITEMS_IN_QUERY);
1102 relations[relationCnt++] = relation;
1103 }
1104
1107 void add_src_entity(Entity entity) {
1108 if (entity == EntityBad || core::has(src_entities_view(), entity))
1109 return;
1110
1111 GAIA_ASSERT(sourceEntityCnt < MAX_ITEMS_IN_QUERY);
1112 sourceEntities[sourceEntityCnt++] = entity;
1113 }
1114
1117 GAIA_NODISCARD bool can_reuse_src_cache() const {
1119 }
1120 };
1121
1131
1143 uint8_t idsCnt = 0;
1145 uint8_t changedCnt = 0;
1155 TSortByFunc sortByFunc;
1159 TGroupByFunc groupByFunc;
1161 QueryMask queryMask;
1164 uint32_t as_mask_0;
1167 uint32_t as_mask_1;
1169 uint8_t firstNot;
1171 uint8_t firstAny;
1173 uint8_t firstOr;
1175 uint8_t groupDepCnt = 0;
1180 uint16_t flags;
1182 uint16_t cacheSrcTrav = 0;
1205
1208 GAIA_NODISCARD bool has_match_terms() const {
1209 return (flags & QueryFlags::HasMatchTerms) != 0;
1210 }
1211
1214 GAIA_NODISCARD std::span<const Entity> ids_view() const {
1215 return {ids.data(), idsCnt};
1216 }
1217
1220 GAIA_NODISCARD std::span<const Entity> changed_view() const {
1221 return {changed.data(), changedCnt};
1222 }
1223
1226 GAIA_NODISCARD std::span<const uint8_t> changed_fields_view() const {
1227 return {changedFields.data(), changedCnt};
1228 }
1229
1232 GAIA_NODISCARD std::span<const Entity> group_deps_view() const {
1233 return {groupDeps.data(), groupDepCnt};
1234 }
1235
1238 GAIA_NODISCARD std::span<const QueryTerm> lookup_terms_view() const {
1240 }
1241
1244 GAIA_NODISCARD bool uses_term_access_identity() const {
1246 (idsCnt != 0 && lookup_terms_view().back().op == QueryOpKind::Any);
1247 }
1248
1251 GAIA_NODISCARD std::span<QueryTerm> lookup_terms_view_mut() {
1253 }
1254
1257 GAIA_NODISCARD std::span<const Entity> changed_lookup_view() const {
1259 }
1260
1263 GAIA_NODISCARD std::span<Entity> changed_lookup_view_mut() {
1265 }
1266
1269 GAIA_NODISCARD std::span<const Entity> group_deps_lookup_view() const {
1271 }
1272
1275 GAIA_NODISCARD std::span<Entity> group_deps_lookup_view_mut() {
1277 }
1278
1281 void add_group_dep(Entity relation) {
1282 if (relation == EntityBad || core::has(group_deps_view(), relation))
1283 return;
1284
1285 GAIA_ASSERT(groupDepCnt < MAX_ITEMS_IN_QUERY);
1286 if (groupDepCnt < MAX_ITEMS_IN_QUERY)
1287 groupDeps[groupDepCnt++] = relation;
1288 }
1289
1296
1297 const bool hasBuiltInGroupDep = groupBy != EntityBad && (groupByFunc == group_by_func_default ||
1298 groupByFunc == group_by_func_depth_order);
1299 if (hasBuiltInGroupDep)
1301 for (auto relation: group_deps_view())
1302 deps.add_rel(relation);
1303 }
1304
1307 GAIA_NODISCARD std::span<QueryTerm> terms_view_mut() {
1308 return {terms.data(), idsCnt};
1309 }
1312 GAIA_NODISCARD std::span<const QueryTerm> terms_view() const {
1313 return {terms.data(), idsCnt};
1314 }
1315
1321
1322 const bool hasVariables = deps.has_dep_flag(DependencyHasVariableTerms);
1323 const bool hasSources = deps.has_dep_flag(DependencyHasSourceTerms);
1324 const bool hasTraversal = deps.has_dep_flag(DependencyHasTraversalTerms);
1325 const bool hasRelations = deps.relationCnt != 0;
1326 if (hasSources && hasTraversal && !hasVariables)
1328
1329 const uint8_t depCnt = (uint8_t)hasVariables + (uint8_t)hasSources + (uint8_t)hasRelations;
1330 if (depCnt > 1)
1332 if (hasVariables)
1334 if (hasSources)
1336 if (hasRelations)
1339 }
1340
1346
1352
1355 GAIA_NODISCARD bool calc_can_reuse_dynamic_cache() const {
1357 return false;
1358
1359 switch (dynamicCacheKind) {
1361 return false;
1364 return true;
1366 return deps.can_reuse_src_cache();
1368 return deps.can_reuse_src_cache() && cacheSrcTrav != 0;
1371 return true;
1373 return false;
1375 }
1376
1377 GAIA_ASSERT(false);
1378 return false;
1379 }
1380
1383 GAIA_FOR(idsCnt) lookupIdentity.lookupTerms[i] = terms[i];
1384 canonicalize_lookup_terms(lookup_terms_view_mut());
1385 GAIA_FOR(changedCnt) lookupIdentity.changed[i] = changed[i];
1386 canonicalize_lookup_changed(changed_lookup_view_mut());
1388 canonicalize_lookup_changed(group_deps_lookup_view_mut());
1389 }
1390
1394 GAIA_NODISCARD bool lookup_keys_equal(const Data& other) const {
1395 {
1396 const auto left = lookup_terms_view();
1397 const auto right = other.lookup_terms_view();
1398 GAIA_FOR((uint32_t)left.size()) {
1399 if (left[i] != right[i])
1400 return false;
1401 }
1402 }
1403
1405 const auto left = lookup_terms_view();
1406 const auto right = other.lookup_terms_view();
1407 GAIA_FOR((uint32_t)left.size()) {
1408 if (left[i].access != right[i].access || left[i].fieldIndex != right[i].fieldIndex)
1409 return false;
1410 }
1411 }
1412
1413 {
1414 const auto left = changed_lookup_view();
1415 const auto right = other.changed_lookup_view();
1416 GAIA_FOR((uint32_t)left.size()) {
1417 if (left[i] != right[i])
1418 return false;
1419 }
1420 }
1421
1422 {
1423 const auto left = group_deps_lookup_view();
1424 const auto right = other.group_deps_lookup_view();
1425 GAIA_FOR((uint32_t)left.size()) {
1426 if (left[i] != right[i])
1427 return false;
1428 }
1429 }
1430
1431 return true;
1432 }
1433
1437 QueryLookupHash::Type payloadHash = 0;
1438 const bool useTermAccess = uses_term_access_identity();
1439
1440 // Ids & ops
1441 {
1442 QueryLookupHash::Type hash = 0;
1443
1444 for (const auto& pair: lookup_terms_view()) {
1445 const auto op = (QueryLookupHash::Type)pair.op;
1446 const auto access = !useTermAccess ? op
1447 : (op | ((QueryLookupHash::Type)pair.access << 8) |
1448 ((QueryLookupHash::Type)pair.fieldIndex << 16));
1449 hash = core::hash_combine(hash, access);
1450 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.id.value());
1451 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.src.value());
1452 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.entTrav.value());
1453 hash = core::hash_combine(hash, (QueryLookupHash::Type)(uint8_t)pair.travKind);
1454 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.travDepth);
1455 hash = core::hash_combine(hash, (QueryLookupHash::Type)(uint8_t)pair.matchKind);
1456 }
1457 hash = core::hash_combine(hash, (QueryLookupHash::Type)idsCnt);
1458 hash = core::hash_combine(hash, (QueryLookupHash::Type)(!useTermAccess ? readWriteMask : 0));
1459 hash = core::hash_combine(hash, (QueryLookupHash::Type)cacheSrcTrav);
1460
1461 const bool matchPrefab = (flags & QueryFlags::MatchPrefab) != 0;
1462 hash = core::hash_combine(hash, (QueryLookupHash::Type)matchPrefab);
1463
1464 payloadHash = hash;
1465 }
1466
1467 // Filters
1468 {
1469 QueryLookupHash::Type hash = 0;
1470
1471 for (const auto entity: changed_lookup_view())
1472 hash = core::hash_combine(hash, (QueryLookupHash::Type)entity.value());
1473 hash = core::hash_combine(hash, (QueryLookupHash::Type)changedCnt);
1474
1475 payloadHash = core::hash_combine(payloadHash, hash);
1476 }
1477
1478 // Explicit grouping dependencies
1479 {
1480 QueryLookupHash::Type hash = 0;
1481
1482 for (const auto entity: group_deps_lookup_view())
1483 hash = core::hash_combine(hash, (QueryLookupHash::Type)entity.value());
1484 hash = core::hash_combine(hash, (QueryLookupHash::Type)groupDepCnt);
1485
1486 payloadHash = core::hash_combine(payloadHash, hash);
1487 }
1488
1489 return payloadHash;
1490 }
1491
1495 GAIA_NODISCARD bool grouping_payload_equal(const Data& other) const {
1496 if (groupBy != other.groupBy)
1497 return false;
1498 if (groupByFunc != other.groupByFunc)
1499 return false;
1501 }
1502
1506 GAIA_NODISCARD bool sort_payload_equal(const Data& other) const {
1507 return sortBy == other.sortBy && sortByFunc == other.sortByFunc;
1508 }
1509
1512 GAIA_NODISCARD bool has_sort_payload() const {
1513 return sortBy != EntityBad || sortByFunc != nullptr;
1514 }
1515
1519 QueryLookupHash::Type hash = 0;
1520 hash = core::hash_combine(hash, (QueryLookupHash::Type)sortBy.value());
1521 hash = core::hash_combine(hash, (QueryLookupHash::Type)sortByFunc);
1522 return hash;
1523 }
1524
1528 GAIA_NODISCARD bool identity_payload_equal(const Data& other) const {
1529 if (idsCnt != other.idsCnt)
1530 return false;
1531 if (changedCnt != other.changedCnt)
1532 return false;
1533 if (groupDepCnt != other.groupDepCnt)
1534 return false;
1535 if (has_match_terms() != other.has_match_terms())
1536 return false;
1538 return false;
1539 if (cacheSrcTrav != other.cacheSrcTrav)
1540 return false;
1541 if (!lookup_keys_equal(other))
1542 return false;
1543 if (!sort_payload_equal(other))
1544 return false;
1545 return grouping_payload_equal(other);
1546 }
1547
1551 QueryLookupHash::Type hash = 0;
1552 hash = core::hash_combine(hash, (QueryLookupHash::Type)groupBy.value());
1553 hash = core::hash_combine(hash, (QueryLookupHash::Type)groupByFunc);
1554 hash = core::hash_combine(hash, (QueryLookupHash::Type)((flags & QueryFlags::OrderGroups) != 0));
1555 return hash;
1556 }
1557
1562 if (has_sort_payload())
1563 hash = core::hash_combine(hash, hash_sort_payload());
1564 return core::hash_combine(hash, hash_grouping_payload());
1565 }
1566
1569 GAIA_NODISCARD QueryLookupHash calc_lookup_hash() const {
1570 return {core::calculate_hash64(hash_identity_payload())};
1571 }
1572 } data{};
1573 // Make sure that MAX_ITEMS_IN_QUERY can fit into data.readWriteMask
1574 static_assert(MAX_ITEMS_IN_QUERY < 16);
1575
1578 void init(World* pWorld) {
1579 w = pWorld;
1580 cc = &comp_cache_mut(*pWorld);
1581 }
1582
1584 void refresh() {
1585#if GAIA_ASSERT_ENABLED
1586 // Sorting reads component values even when the callback does not.
1587 if (data.has_match_terms() && data.sortByFunc != nullptr) {
1588 GAIA_FOR(data.idsCnt) {
1589 GAIA_ASSERT(data.terms[i].access != QueryAccess::Match || data.terms[i].id != data.sortBy);
1590 }
1591 }
1592#endif
1593 const auto mask0_old = data.as_mask_0;
1594 const auto mask1_old = data.as_mask_1;
1595 const auto isComplex_old = data.flags & QueryFlags::Complex;
1596 const auto hasSourceTerms_old = data.flags & QueryFlags::HasSourceTerms;
1597 const auto hasVariableTerms_old = data.flags & QueryFlags::HasVariableTerms;
1598 const auto cachePolicy_old = data.cachePolicy;
1599 const auto createArchetypeMatchKind_old = data.createArchetypeMatchKind;
1600 const auto dynamicCacheKind_old = data.dynamicCacheKind;
1601 const auto canDirectTargetEval_old = data.canDirectTargetEval;
1602 const auto canDirectEntitySeedEvalShape_old = data.canDirectEntitySeedEvalShape;
1603 const auto hasOnlyDirectOrTerms_old = data.hasOnlyDirectOrTerms;
1604 const auto canReuseDynamicCache_old = data.canReuseDynamicCache;
1605 const auto dependencyFlags_old = data.deps.flags;
1606 const auto createSelectorCnt_old = data.deps.createSelectorCnt;
1607 const auto exclusionCnt_old = data.deps.exclusionCnt;
1608 const auto relationCnt_old = data.deps.relationCnt;
1609 const auto sourceEntityCnt_old = data.deps.sourceEntityCnt;
1610 const auto sourceTermCnt_old = data.deps.sourceTermCnt;
1611 auto createSelectors_old = data.deps.createSelectors;
1612 auto exclusions_old = data.deps.exclusions;
1613 auto relations_old = data.deps.relations;
1614 auto sourceEntities_old = data.deps.sourceEntities;
1615
1616 // Update masks
1617 {
1618 uint32_t as_mask_0 = 0;
1619 uint32_t as_mask_1 = 0;
1620 bool isComplex = false;
1621 bool hasSourceTerms = false;
1622 bool hasVariableTerms = false;
1623 bool hasPrefabTerms = false;
1624 bool hasCreateSelector = false;
1625 bool canDirectCreateArchetypeMatch = true;
1626 bool hasEntityFilterTerms = false;
1627 bool canDirectTargetEval = true;
1628 bool hasOnlyDirectOrTerms = true;
1629 bool hasOrTerms = false;
1630 bool hasDirectTargetEvalPositiveTerms = false;
1631 const QueryTerm* pSingleDirectTargetAllTerm = nullptr;
1632 bool singleDirectTargetEvalPossible = true;
1633 bool directTargetEvalHasRemainingTerms = false;
1634 bool directTargetEvalAllTermsDirect = true;
1635 QueryEntityArray idsNoSrc;
1636 QueryEntityArray createSelectorsAll;
1637 QueryEntityArray createSelectorsOr;
1638 uint32_t idsNoSrcCnt = 0;
1639 uint8_t createSelectorAllCnt = 0;
1640 uint8_t createSelectorOrCnt = 0;
1641 data.deps.clear();
1643 data.directTargetEvalId = EntityBad;
1644 if (data.sortByFunc != nullptr)
1646 if (data.groupBy != EntityBad)
1648
1649 auto terms = data.terms_view();
1650 const auto cnt = (uint32_t)terms.size();
1651 GAIA_FOR(cnt) {
1652 const auto& term = terms[i];
1653 const auto id = term.id;
1654 hasPrefabTerms |= id == Prefab;
1655 const bool isDirectIsTerm = term.src == EntityBad && term.entTrav == EntityBad &&
1656 !term_has_variables(term) && term.matchKind != QueryMatchKind::Direct &&
1657 id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()) &&
1658 !is_variable((EntityId)id.gen());
1659 const bool isPotentialInheritedTerm = query_term_uses_potential_inherited_id_matching(term);
1660 const bool isInheritedTerm = isPotentialInheritedTerm && world_term_uses_inherit_policy(*w, id);
1661 directTargetEvalAllTermsDirect &= !isDirectIsTerm && !isInheritedTerm;
1662 const bool isNonFragmentingTerm =
1663 term.src == EntityBad && term.entTrav == EntityBad && !term_has_variables(term) &&
1664 ((id.pair() && world_relation_uses_non_fragmenting_storage(*w, pair_rel(*w, id))) ||
1665 (!id.pair() && world_component_is_non_fragmenting(*w, id)));
1666 hasEntityFilterTerms |= isNonFragmentingTerm || isDirectIsTerm || isInheritedTerm;
1667 }
1668
1669 GAIA_FOR(cnt) {
1670 const auto& term = terms[i];
1671 const auto id = term.id;
1672 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term)) {
1673 singleDirectTargetEvalPossible = false;
1674 canDirectTargetEval = false;
1675 hasOnlyDirectOrTerms = false;
1676 }
1677 switch (term.op) {
1678 case QueryOpKind::All:
1679 hasDirectTargetEvalPositiveTerms = true;
1680 if (pSingleDirectTargetAllTerm == nullptr)
1681 pSingleDirectTargetAllTerm = &term;
1682 else
1683 singleDirectTargetEvalPossible = false;
1684 hasOnlyDirectOrTerms = false;
1685 break;
1686 case QueryOpKind::Or:
1687 hasDirectTargetEvalPositiveTerms = true;
1688 hasOrTerms = true;
1689 directTargetEvalHasRemainingTerms = true;
1690 break;
1691 case QueryOpKind::Not:
1692 directTargetEvalHasRemainingTerms = true;
1693 break;
1694 case QueryOpKind::Any:
1695 case QueryOpKind::Count:
1696 singleDirectTargetEvalPossible = false;
1697 canDirectTargetEval = false;
1698 hasOnlyDirectOrTerms = false;
1699 break;
1700 }
1701 const bool isDirectIsTerm = term.src == EntityBad && term.entTrav == EntityBad &&
1702 !term_has_variables(term) && term.matchKind != QueryMatchKind::Direct &&
1703 id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()) &&
1704 !is_variable((EntityId)id.gen());
1705 const bool isPotentialInheritedTerm = query_term_uses_potential_inherited_id_matching(term);
1706 const bool isInheritedTerm = isPotentialInheritedTerm && world_term_uses_inherit_policy(*w, id);
1707 const bool isCachedInheritedDataTerm = isInheritedTerm && !world_component_uses_sparse_storage(*w, id);
1708 const bool isNonFragmentingTerm =
1709 term.src == EntityBad && term.entTrav == EntityBad && !term_has_variables(term) &&
1710 ((id.pair() && world_relation_uses_non_fragmenting_storage(*w, pair_rel(*w, id))) ||
1711 (!id.pair() && world_component_is_non_fragmenting(*w, id)));
1712 canDirectCreateArchetypeMatch &= term.src == EntityBad;
1713 if (id.pair() && (is_wildcard(id.id()) || is_wildcard(id.gen())))
1715 const bool hasDynamicRelationUsage =
1716 term.entTrav != EntityBad || term.src != EntityBad || term_has_variables(term);
1717 if (id.pair() && hasDynamicRelationUsage && !is_wildcard(id.id()) && !is_variable((EntityId)id.id()))
1718 data.deps.add_rel(pair_rel(*w, id));
1719 if (term.entTrav != EntityBad) {
1720 data.deps.add_rel(term.entTrav);
1722 }
1723 if (term.src != EntityBad) {
1724 hasSourceTerms = true;
1727 if (!is_variable(term.src))
1728 data.deps.add_src_entity(term.src);
1729 }
1730
1731 if (term_has_variables(term)) {
1732 hasVariableTerms = true;
1734 isComplex = true;
1735 continue;
1736 }
1737
1738 if (isPotentialInheritedTerm)
1740
1741 if (isNonFragmentingTerm || isDirectIsTerm || isInheritedTerm) {
1743 if (isCachedInheritedDataTerm)
1745 if (id.pair() && !is_wildcard(id.id()) && !is_variable((EntityId)id.id()))
1746 data.deps.add_rel(pair_rel(*w, id));
1747 continue;
1748 }
1749
1750 if (hasEntityFilterTerms && term.op == QueryOpKind::Or) {
1751 isComplex = true;
1752 continue;
1753 }
1754
1755 // Source terms are evaluated separately by the VM.
1756 // They should not affect archetype-level query masks.
1757 if (term.src != EntityBad) {
1758 continue;
1759 }
1760
1761 // ANY terms are not hard requirements and must not affect archetype prefilter masks.
1762 if (term.op != QueryOpKind::Any)
1763 idsNoSrc[idsNoSrcCnt++] = id;
1764
1765 if (term.op == QueryOpKind::All || term.op == QueryOpKind::Or) {
1766 hasCreateSelector = true;
1768 if (term.op == QueryOpKind::All)
1769 createSelectorsAll[createSelectorAllCnt++] = id;
1770 else
1771 createSelectorsOr[createSelectorOrCnt++] = id;
1772 } else if (term.op == QueryOpKind::Not) {
1775 } else if (term.op == QueryOpKind::Any) {
1777 }
1778
1779 // Build the Is mask.
1780 // We will use it to identify entities with an Is relationship quickly.
1781 const bool allowSemanticIs = !(
1782 term.matchKind == QueryMatchKind::Direct && id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()));
1783 if (!id.pair()) {
1784 const auto j = (uint32_t)i;
1785 const auto has_as = allowSemanticIs ? (uint32_t)is_base(*w, id) : 0U;
1786 as_mask_0 |= (has_as << j);
1787 } else {
1788 const bool idIsWildcard = is_wildcard(id.id());
1789 const bool isGenWildcard = is_wildcard(id.gen());
1790 isComplex |= (idIsWildcard || isGenWildcard);
1791
1792 if (!idIsWildcard) {
1793 const auto j = (uint32_t)i;
1794 const auto e = pair_rel(*w, id);
1795 const auto has_as = allowSemanticIs ? (uint32_t)is_base(*w, e) : 0U;
1796 as_mask_0 |= (has_as << j);
1797 }
1798
1799 if (!isGenWildcard) {
1800 const auto j = (uint32_t)i;
1801 const auto e = pair_tgt(*w, id);
1802 const auto has_as = allowSemanticIs ? (uint32_t)is_base(*w, e) : 0U;
1803 as_mask_1 |= (has_as << j);
1804 }
1805 }
1806 }
1807
1808 if (singleDirectTargetEvalPossible && pSingleDirectTargetAllTerm != nullptr) {
1809 const auto& term = *pSingleDirectTargetAllTerm;
1810 const auto id = term.id;
1811 if (term.matchKind == QueryMatchKind::In && id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()) &&
1812 !is_variable((EntityId)id.gen())) {
1813 data.directTargetEvalKind = directTargetEvalHasRemainingTerms ? DirectTargetEvalKind::SingleAllInIsMixed
1815 } else if (
1816 term.matchKind == QueryMatchKind::Semantic && id.pair() && id.id() == Is.id() &&
1817 !is_wildcard(id.gen()) && !is_variable((EntityId)id.gen())) {
1818 data.directTargetEvalKind = directTargetEvalHasRemainingTerms
1821 } else if (
1822 term.matchKind == QueryMatchKind::Semantic && !is_wildcard(id) && !is_variable((EntityId)id.id()) &&
1823 (!id.pair() || !is_variable((EntityId)id.gen())) && world_term_uses_inherit_policy(*w, id)) {
1824 data.directTargetEvalKind = directTargetEvalHasRemainingTerms
1827 } else {
1828 data.directTargetEvalKind = !directTargetEvalHasRemainingTerms ? DirectTargetEvalKind::SingleAllDirect
1829 : directTargetEvalAllTermsDirect
1832 }
1834 }
1835 data.canDirectTargetEval = canDirectTargetEval && hasDirectTargetEvalPositiveTerms;
1837 data.canDirectTargetEval && data.sortByFunc == nullptr && data.groupBy == EntityBad;
1838 data.hasOnlyDirectOrTerms = hasOnlyDirectOrTerms && hasOrTerms;
1839
1840 // Update the mask
1841 data.as_mask_0 = as_mask_0;
1842 data.as_mask_1 = as_mask_1;
1844 if (createSelectorAllCnt != 0) {
1845 auto selector_rank = [](Entity term) {
1846 if (!term.pair())
1847 return 2;
1848 if (!is_wildcard(term.id()) && !is_wildcard(term.gen()))
1849 return 0;
1850 if (is_wildcard(term.id()) && is_wildcard(term.gen()))
1851 return 3;
1852 return 1;
1853 };
1854
1855 // For immediate structural queries, we choose one required ALL selector as the create-time wake-up key.
1856 // This choice is ordered by:
1857 // 1) smaller component index bucket size first
1858 // 2) if equal, more specific selector first
1859 uint8_t bestIdx = 0;
1860 auto bestBucketSize = world_component_index_bucket_size(*w, createSelectorsAll[0]);
1861 auto bestRank = selector_rank(createSelectorsAll[0]);
1862 GAIA_FOR2_(1, createSelectorAllCnt, i) {
1863 const auto bucketSize = world_component_index_bucket_size(*w, createSelectorsAll[i]);
1864 const auto rank = selector_rank(createSelectorsAll[i]);
1865 if (bucketSize < bestBucketSize || (bucketSize == bestBucketSize && rank < bestRank)) {
1866 bestBucketSize = bucketSize;
1867 bestRank = rank;
1868 bestIdx = (uint8_t)i;
1869 }
1870 }
1871 data.deps.createSelectors[data.deps.createSelectorCnt++] = createSelectorsAll[bestIdx];
1872 } else {
1873 GAIA_FOR(createSelectorOrCnt) {
1874 data.deps.createSelectors[data.deps.createSelectorCnt++] = createSelectorsOr[i];
1875 }
1876 }
1877 if (hasPrefabTerms)
1879 else
1881
1882 if (hasSourceTerms)
1884 else
1886
1887 if (hasVariableTerms)
1889 else
1891
1892 if (hasSourceTerms || hasVariableTerms)
1894 else if (
1895 !hasEntityFilterTerms && data.sortByFunc == nullptr && data.groupBy == EntityBad && hasCreateSelector)
1897 else
1899
1900 data.createArchetypeMatchKind = data.cachePolicy == CachePolicy::Immediate && canDirectCreateArchetypeMatch
1904
1905 // Traversed-source snapshot caching is only effective for traversed source terms.
1907 data.cacheSrcTrav = 0;
1908
1910
1911 // Calculate the component mask for simple queries
1912 isComplex |= ((data.as_mask_0 + data.as_mask_1) != 0);
1913 if (isComplex) {
1914 data.queryMask = {};
1916 } else {
1917 data.queryMask = build_entity_mask(EntitySpan{idsNoSrc.data(), idsNoSrcCnt});
1919 }
1920 }
1921
1922 // Request recompilation of the query if the mask has changed
1924
1925 // Request recompilation of the query if the mask has changed
1926 if (mask0_old != data.as_mask_0 || mask1_old != data.as_mask_1 ||
1927 isComplex_old != (data.flags & QueryFlags::Complex) ||
1928 hasSourceTerms_old != (data.flags & QueryFlags::HasSourceTerms) ||
1929 hasVariableTerms_old != (data.flags & QueryFlags::HasVariableTerms) ||
1930 canDirectTargetEval_old != data.canDirectTargetEval ||
1931 canDirectEntitySeedEvalShape_old != data.canDirectEntitySeedEvalShape ||
1932 hasOnlyDirectOrTerms_old != data.hasOnlyDirectOrTerms ||
1933 canReuseDynamicCache_old != data.canReuseDynamicCache || cachePolicy_old != data.cachePolicy ||
1934 createArchetypeMatchKind_old != data.createArchetypeMatchKind ||
1935 dynamicCacheKind_old != data.dynamicCacheKind || dependencyFlags_old != data.deps.flags ||
1936 createSelectorCnt_old != data.deps.createSelectorCnt || exclusionCnt_old != data.deps.exclusionCnt ||
1937 relationCnt_old != data.deps.relationCnt || sourceEntityCnt_old != data.deps.sourceEntityCnt ||
1938 sourceTermCnt_old != data.deps.sourceTermCnt || createSelectors_old != data.deps.createSelectors ||
1939 exclusions_old != data.deps.exclusions || relations_old != data.deps.relations ||
1940 sourceEntities_old != data.deps.sourceEntities)
1942 }
1943
1948 GAIA_NODISCARD static bool
1949 equals_no_handle_assumption(const QueryCtx& leftCtx, const QueryCtx& rightCtx) noexcept {
1950 // Lookup hash must match
1951 if (leftCtx.hashLookup != rightCtx.hashLookup)
1952 return false;
1953
1954 const auto& left = leftCtx.data;
1955 const auto& right = rightCtx.data;
1956 return left.identity_payload_equal(right);
1957 }
1958
1962 GAIA_NODISCARD bool operator==(const QueryCtx& other) const noexcept {
1963 // Comparison expected to be done only the first time the query is set up
1964 GAIA_ASSERT(q.handle.id() == QueryIdBad);
1965 // Fast path when cache ids are set
1966 // if (queryId != QueryIdBad && queryId == other.queryId)
1967 // return true;
1968
1969 return equals_no_handle_assumption(*this, other);
1970 }
1971
1975 GAIA_NODISCARD bool operator!=(const QueryCtx& other) const noexcept {
1976 return !operator==(other);
1977 }
1978 };
1979
1986 constexpr bool operator()(const QueryTerm& lhs, const QueryTerm& rhs) const {
1987 return query_term_less_for_lookup(lhs, rhs);
1988 }
1989 };
1990
1993 inline void sort(QueryCtx& ctx) {
1994 const uint32_t idsCnt = ctx.data.idsCnt;
1995 const uint32_t changedCnt = ctx.data.changedCnt;
1996
1997 auto& ctxData = ctx.data;
1998 // Canonicalize degenerate OR queries: a single OR term has AND semantics.
1999 // Rewriting it here keeps ordering/hash behavior identical to an explicit ALL term.
2000 if (idsCnt > 0) {
2001 uint32_t orCnt = 0;
2002 uint32_t orIdx = BadIndex;
2003 GAIA_FOR(idsCnt) {
2004 if (ctxData.terms[i].op != QueryOpKind::Or)
2005 continue;
2006 ++orCnt;
2007 orIdx = i;
2008 if (orCnt > 1)
2009 break;
2010 }
2011
2012 if (orCnt == 1)
2013 ctxData.terms[orIdx].op = QueryOpKind::All;
2014 }
2015
2016 // Sort data. Necessary for correct hash calculation.
2017 // Without sorting query.all<XXX, YYY> would be different than query.all<YYY, XXX>.
2018 // Also makes sure data is in optimal order for query processing.
2019 core::sort(
2020 ctxData.terms.data(), ctxData.terms.data() + ctxData.idsCnt, query_sort_cond{}, //
2021 [&](uint32_t left, uint32_t right) {
2022 core::swap(ctxData.ids[left], ctxData.ids[right]);
2023 core::swap(ctxData.terms[left], ctxData.terms[right]);
2024
2025 // Make sure masks remains correct after sorting
2026 core::swap_bits(ctxData.readWriteMask, left, right);
2027 core::swap_bits(ctxData.as_mask_0, left, right);
2028 core::swap_bits(ctxData.as_mask_1, left, right);
2029 });
2030
2031 if (idsCnt > 0) {
2032 uint32_t i = 0;
2033 while (i < idsCnt && ctxData.terms[i].op == QueryOpKind::All)
2034 ++i;
2035 ctxData.firstOr = (uint8_t)i;
2036 while (i < idsCnt && ctxData.terms[i].op == QueryOpKind::Or)
2037 ++i;
2038 ctxData.firstNot = (uint8_t)i;
2039 while (i < idsCnt && ctxData.terms[i].op == QueryOpKind::Not)
2040 ++i;
2041 ctxData.firstAny = (uint8_t)i;
2042 } else
2043 ctxData.firstOr = ctxData.firstNot = ctxData.firstAny = 0;
2044
2045 // Canonicalize filter order. This enables monotonic component lookup in filter matching
2046 // and keeps cache keys stable regardless of changed() call order.
2047 if (changedCnt > 1) {
2048 core::sort(ctxData.changed.data(), ctxData.changed.data() + changedCnt, SortComponentCond{});
2049 }
2050
2051 GAIA_FOR(changedCnt) {
2052 const auto comp = ctxData.changed[i];
2053 uint32_t compIdx = 0;
2054 while (compIdx < idsCnt && ctxData.ids[compIdx] != comp)
2055 ++compIdx;
2056
2057 GAIA_ASSERT(compIdx < idsCnt);
2058 ctxData.changedFields[i] = compIdx < idsCnt ? (uint8_t)compIdx : (uint8_t)0xFF;
2059 }
2060 }
2061
2065 inline void normalize_cache_src_trav(QueryCtx& ctx) {
2066 auto& ctxData = ctx.data;
2067 if (ctxData.cacheSrcTrav == 0)
2068 return;
2069
2070 bool hasTraversedSourceTerm = false;
2071 for (const auto& term: ctxData.terms_view()) {
2072 if (term.src == EntityBad || term.entTrav == EntityBad)
2073 continue;
2074
2075 hasTraversedSourceTerm = true;
2076 break;
2077 }
2078
2079 if (!hasTraversedSourceTerm)
2080 ctxData.cacheSrcTrav = 0;
2081 }
2082
2085 inline void calc_lookup_hash(QueryCtx& ctx) {
2086 GAIA_ASSERT(ctx.cc != nullptr);
2087 // Make sure we don't calculate the hash twice
2088 GAIA_ASSERT(ctx.hashLookup.hash == 0);
2089
2090 ctx.hashLookup = ctx.data.calc_lookup_hash();
2091 }
2092
2100 template <uint32_t MAX_COMPONENTS>
2101 GAIA_NODISCARD inline uint32_t comp_idx(const QueryTerm* pTerms, Entity entity, Entity src) {
2102 // We let the compiler know the upper iteration bound at compile-time.
2103 // This way it can optimize better (e.g. loop unrolling, vectorization).
2104 GAIA_FOR(MAX_COMPONENTS) {
2105 if (pTerms[i].id == entity && pTerms[i].src == src)
2106 return i;
2107 }
2108
2109 GAIA_ASSERT(false);
2110 return BadIndex;
2111 }
2112 } // namespace ecs
2113} // namespace gaia
Array of elements of type.
Definition darray_ext_impl.h:31
void pop_back() noexcept
Removes the last element.
Definition darray_ext_impl.h:400
void push_back(const T &arg)
Appends an element.
Definition darray_ext_impl.h:360
void clear() noexcept
Removes all elements.
Definition darray_ext_impl.h:518
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_ext_impl.h:236
GAIA_NODISCARD bool empty() const noexcept
Checks whether the container has no elements.
Definition darray_ext_impl.h:567
Fixed-shape group of chunks storing entities that share the same component layout....
Definition archetype.h:97
Small reverse-lookup bucket for entity-to-archetype matches. Most buckets contain one archetype,...
Definition query_common.h:186
void push_back(ComponentIndexEntry entry)
Appends a reverse-index record, spilling inline storage when necessary.
Definition query_common.h:293
GAIA_NODISCARD size_type size() const noexcept
Returns the number of reverse-index records.
Definition query_common.h:221
GAIA_NODISCARD ComponentIndexEntry & back() noexcept
Returns the final mutable record.
Definition query_common.h:279
GAIA_NODISCARD bool empty() const noexcept
Checks whether the bucket contains no reverse-index records.
Definition query_common.h:215
GAIA_NODISCARD const ComponentIndexEntry & operator[](size_type idx) const noexcept
Returns a read-only record by index.
Definition query_common.h:272
GAIA_NODISCARD const ComponentIndexEntry * data() const noexcept
Returns read-only contiguous record storage.
Definition query_common.h:233
GAIA_NODISCARD ComponentIndexEntry * end() noexcept
Returns a mutable iterator past the final record.
Definition query_common.h:251
GAIA_NODISCARD ComponentIndexEntry * begin() noexcept
Returns a mutable iterator to the first record.
Definition query_common.h:239
GAIA_NODISCARD ComponentIndexEntry * data() noexcept
Returns mutable contiguous record storage.
Definition query_common.h:227
GAIA_NODISCARD const ComponentIndexEntry * begin() const noexcept
Returns a read-only iterator to the first record.
Definition query_common.h:245
uint32_t size_type
Unsigned element-count and index type.
Definition query_common.h:191
GAIA_NODISCARD const ComponentIndexEntry * end() const noexcept
Returns a read-only iterator past the final record.
Definition query_common.h:257
GAIA_NODISCARD const ComponentIndexEntry & back() const noexcept
Returns the final read-only record.
Definition query_common.h:286
GAIA_NODISCARD ComponentIndexEntry & operator[](size_type idx) noexcept
Returns a mutable record by index.
Definition query_common.h:264
void pop_back()
Removes the final record and restores inline storage when one record remains.
Definition query_common.h:312
Owns entities, components, archetypes, queries, observers, and systems.
Definition world.h:80
Wrapper for two types forming a relationship pair. Depending on what types are used to form a pair it...
Definition id.h:197
Same API as ser_buffer_binary, but backed by fully dynamic storage.
Definition ser_buffer_binary.h:164
uint64_t Type
Underlying hash storage type.
Definition hashing_policy.h:44
T hash
Precomputed hash value.
Definition hashing_policy.h:51
One archetype record stored in the entity-to-archetype reverse index.
Definition query_common.h:168
GAIA_NODISCARD bool matches(const Archetype *pOther) const
Tests whether this record belongs to an archetype.
Definition query_common.h:179
uint16_t matchCount
Number of archetype components contributing this lookup-key match.
Definition query_common.h:174
Archetype * pArchetype
Archetype matched by the indexed entity or pair key.
Definition query_common.h:170
uint16_t compIdx
Component column index in pArchetype, or ComponentIndexBad when no direct column exists.
Definition query_common.h:172
Hashmap lookup structure used for Entity.
Definition id.h:486
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:231
GAIA_NODISCARD constexpr auto gen() const noexcept
Generation index of the entity.
Definition id.h:301
GAIA_NODISCARD constexpr auto value() const noexcept
Raw identifier value.
Definition id.h:325
GAIA_NODISCARD constexpr bool pair() const noexcept
Whether this id refers to a relationship pair.
Definition id.h:313
GAIA_NODISCARD constexpr auto id() const noexcept
Entity index in the entity array.
Definition id.h:295
Explicit component/entity access declarations used for scheduling decisions.
Definition query_common.h:677
GAIA_NODISCARD QueryAccess access(Entity entity) const
Returns explicitly declared access for an id.
Definition query_common.h:732
uint8_t readCnt
Number of valid entries in reads.
Definition query_common.h:683
GAIA_NODISCARD std::span< const Entity > writes_view() const
Returns the explicitly declared write ids.
Definition query_common.h:697
void add_read(Entity entity)
Declares that an id is read.
Definition query_common.h:703
cnt::sarray< Entity, MAX_ITEMS_IN_QUERY > reads
Component/entity ids read by the callback outside the query terms.
Definition query_common.h:679
GAIA_NODISCARD std::span< const Entity > reads_view() const
Returns the explicitly declared read ids.
Definition query_common.h:691
uint8_t writeCnt
Number of valid entries in writes.
Definition query_common.h:685
cnt::sarray< Entity, MAX_ITEMS_IN_QUERY > writes
Component/entity ids written by the callback outside the query terms.
Definition query_common.h:681
bool hasPairPatterns
At least one custom scheduling id has wildcard or variable pair endpoints.
Definition query_common.h:687
void add_write(Entity entity)
Declares that an id is written.
Definition query_common.h:717
Incremental query-matching cursor for one entity-to-archetype lookup bucket.
Definition query_common.h:152
uint32_t index
Number of bucket records that were already matched at revision.
Definition query_common.h:154
uint32_t revision
Lookup-bucket revision associated with index.
Definition query_common.h:156
Deduplicated entities and flags that can invalidate or update a query cache.
Definition query_common.h:1026
void set_dep_flag(DependencyFlags dependency)
Records a dependency fact.
Definition query_common.h:1084
GAIA_NODISCARD std::span< const Entity > exclusions_view() const
Returns negative selector ids used during archetype creation.
Definition query_common.h:1066
GAIA_NODISCARD bool has_dep_flag(DependencyFlags dependency) const
Tests whether a dependency fact was recorded.
Definition query_common.h:1091
DependencyFlags flags
Combined DependencyFlags describing the compiled query shape.
Definition query_common.h:1046
GAIA_NODISCARD std::span< const Entity > create_selectors_view() const
Returns positive selector ids used during archetype creation.
Definition query_common.h:1060
uint8_t sourceEntityCnt
Number of valid entries in sourceEntities.
Definition query_common.h:1042
GAIA_NODISCARD std::span< const Entity > src_entities_view() const
Returns concrete source-entity dependencies.
Definition query_common.h:1078
GAIA_NODISCARD std::span< const Entity > relations_view() const
Returns relation-version dependencies.
Definition query_common.h:1072
QueryEntityArray relations
Relations whose topology versions affect cached results.
Definition query_common.h:1032
uint8_t sourceTermCnt
Number of fixed-source terms, including duplicate source entities.
Definition query_common.h:1044
void clear()
Resets dependency counts and flags while retaining fixed storage.
Definition query_common.h:1049
QueryEntityArray createSelectors
Positive selector ids used for archetype-create propagation.
Definition query_common.h:1028
uint8_t createSelectorCnt
Number of valid entries in createSelectors.
Definition query_common.h:1036
uint8_t relationCnt
Number of valid entries in relations.
Definition query_common.h:1040
QueryEntityArray sourceEntities
Concrete source entities whose archetype versions affect cached results.
Definition query_common.h:1034
void add_rel(Entity relation)
Adds a unique relation-version dependency.
Definition query_common.h:1097
uint8_t exclusionCnt
Number of valid entries in exclusions.
Definition query_common.h:1038
GAIA_NODISCARD bool can_reuse_src_cache() const
Checks whether each fixed-source term has a distinct tracked entity.
Definition query_common.h:1117
void add_src_entity(Entity entity)
Adds a unique concrete source-entity dependency.
Definition query_common.h:1107
QueryEntityArray exclusions
Negative selector ids used to reject new archetypes.
Definition query_common.h:1030
Cold canonical payload used exclusively by shared-query hashing and equality.
Definition query_common.h:1123
QueryEntityArray changed
Canonicalized changed-filter ids reused by hash/equality for shared query dedup.
Definition query_common.h:1127
cnt::sarray< QueryTerm, MAX_ITEMS_IN_QUERY > lookupTerms
Canonicalized lookup terms reused by hash/equality for shared query dedup.
Definition query_common.h:1125
QueryEntityArray groupDeps
Canonicalized group dependency ids reused by hash/equality for shared query dedup.
Definition query_common.h:1129
Compact compiled query payload used by matching, identity, and cache maintenance.
Definition query_common.h:1024
uint16_t readWriteMask
Read-write mask. Bit 0 stands for component 0 in component arrays. A set bit means write access is re...
Definition query_common.h:1178
uint8_t changedCnt
Number of valid changed-filter ids and field mappings.
Definition query_common.h:1145
QueryEntityArray groupDeps
Explicit grouping invalidation dependencies for custom group_by callbacks.
Definition query_common.h:1151
Entity sortBy
Entity to sort the archetypes by. EntityBad for no sorting.
Definition query_common.h:1153
TGroupByFunc groupByFunc
Function to use to perform the grouping.
Definition query_common.h:1159
uint8_t idsCnt
Number of valid ids and terms in the fixed-capacity query arrays.
Definition query_common.h:1143
GAIA_NODISCARD DynamicCacheKind calc_dynamic_cache_kind() const
Returns the dynamic-cache dependency shape derived from cache policy and dependencies.
Definition query_common.h:1318
uint32_t as_mask_0
Mask for items with Is relationship pair. If the id is a pair, the first part (id) is written here.
Definition query_common.h:1164
Entity groupBy
Entity to group the archetypes by. EntityBad for no grouping.
Definition query_common.h:1157
bool hasOnlyDirectOrTerms
True when the query contains only direct OR/NOT terms and at least one OR term.
Definition query_common.h:1192
void add_group_dep(Entity relation)
Adds a declared grouping invalidation dependency.
Definition query_common.h:1281
cnt::sarray< uint8_t, MAX_ITEMS_IN_QUERY > changedFields
Query term index for each changed-filter component after query canonicalization.
Definition query_common.h:1149
QueryEntityArray ids
Array of queried ids.
Definition query_common.h:1133
GAIA_NODISCARD QueryLookupHash::Type hash_lookup_key_payload() const
Returns the hash contribution from canonical lookup-key payload arrays.
Definition query_common.h:1436
GAIA_NODISCARD bool uses_src_trav_snapshot() const
Returns whether reusable dynamic-cache checks use a traversed source closure snapshot.
Definition query_common.h:1349
GAIA_NODISCARD bool has_sort_payload() const
Returns true when sort identity payload is active.
Definition query_common.h:1512
uint32_t as_mask_1
Mask for items with Is relationship pair. If the id is a pair, the second part (gen) is written here.
Definition query_common.h:1167
bool canDirectEntitySeedEvalShape
True when the query shape is eligible for direct entity seed evaluation.
Definition query_common.h:1190
uint8_t firstOr
First OR record in pairs/ids/ops.
Definition query_common.h:1173
uint16_t cacheSrcTrav
Maximum allowed size of an explicitly cached traversed-source lookup closure.
Definition query_common.h:1182
GAIA_NODISCARD QueryLookupHash::Type hash_sort_payload() const
Returns the hash contribution from sort identity payload.
Definition query_common.h:1518
GAIA_NODISCARD bool uses_term_access_identity() const
Returns whether canonical term access and field order define query identity.
Definition query_common.h:1244
QueryMask queryMask
Component mask used for faster matching of simple queries.
Definition query_common.h:1161
uint8_t firstAny
First ANY record in pairs/ids/ops.
Definition query_common.h:1171
GAIA_NODISCARD std::span< QueryTerm > lookup_terms_view_mut()
Returns mutable canonicalized lookup terms used by shared query deduplication.
Definition query_common.h:1251
GAIA_NODISCARD bool identity_payload_equal(const Data &other) const
Returns true when the shared query identity payload matches another query context payload.
Definition query_common.h:1528
GAIA_NODISCARD std::span< const Entity > changed_lookup_view() const
Returns canonicalized changed-filter lookup ids used by shared query deduplication.
Definition query_common.h:1257
void refresh_lookup_keys()
Refreshes canonical lookup arrays used by shared query deduplication.
Definition query_common.h:1382
GAIA_NODISCARD std::span< const QueryTerm > lookup_terms_view() const
Returns canonicalized lookup terms used by shared query deduplication.
Definition query_common.h:1238
GAIA_NODISCARD std::span< const Entity > ids_view() const
Returns authored query ids in canonical execution order.
Definition query_common.h:1214
GAIA_NODISCARD std::span< const Entity > group_deps_view() const
Returns explicit grouping invalidation dependencies.
Definition query_common.h:1232
QueryArchetypeCacheIndexMap lastMatchedArchetypeIdx_All
Index of the last checked archetype in the component-to-archetype map.
Definition query_common.h:1137
CreateArchetypeMatchKind createArchetypeMatchKind
Create-time archetype matcher derived from query shape.
Definition query_common.h:1200
Entity directTargetEvalId
Term id used by the specialized direct-target evaluation shape.
Definition query_common.h:1186
void add_group_deps()
Adds all grouping invalidation relations to the dependency set. Built-in grouping functions are recog...
Definition query_common.h:1294
GAIA_NODISCARD std::span< const uint8_t > changed_fields_view() const
Returns query-term indices matching changed-filter components.
Definition query_common.h:1226
QueryArchetypeCacheIndexMap lastMatchedArchetypeIdx_Or
Incremental lookup cursors for OR selector terms.
Definition query_common.h:1139
GAIA_NODISCARD QueryLookupHash calc_lookup_hash() const
Returns the finalized lookup hash for shared query identity.
Definition query_common.h:1569
GAIA_NODISCARD bool sort_payload_equal(const Data &other) const
Returns true when the sort identity payload matches another query context payload.
Definition query_common.h:1506
GAIA_NODISCARD std::span< const QueryTerm > terms_view() const
Returns compiled terms in execution order.
Definition query_common.h:1312
GAIA_NODISCARD bool uses_direct_src_version_tracking() const
Returns whether reusable dynamic-cache checks use direct source entity archetype versions.
Definition query_common.h:1343
GAIA_NODISCARD std::span< const Entity > group_deps_lookup_view() const
Returns canonicalized group dependency lookup ids used by shared query deduplication.
Definition query_common.h:1269
DynamicCacheKind dynamicCacheKind
Dynamic-cache dependency shape derived from compiled query metadata.
Definition query_common.h:1202
Dependencies deps
Explicit dependency metadata derived from query shape.
Definition query_common.h:1196
GAIA_NODISCARD std::span< Entity > group_deps_lookup_view_mut()
Returns mutable canonicalized group dependency lookup ids used by shared query deduplication.
Definition query_common.h:1275
TSortByFunc sortByFunc
Function to use to perform sorting.
Definition query_common.h:1155
uint16_t flags
Query flags.
Definition query_common.h:1180
bool canDirectTargetEval
True when the query can evaluate concrete target entities directly.
Definition query_common.h:1188
GAIA_NODISCARD std::span< Entity > changed_lookup_view_mut()
Returns mutable canonicalized changed-filter lookup ids used by shared query deduplication.
Definition query_common.h:1263
GAIA_NODISCARD std::span< QueryTerm > terms_view_mut()
Returns mutable compiled terms in execution order.
Definition query_common.h:1307
uint8_t firstNot
First NOT record in pairs/ids/ops.
Definition query_common.h:1169
CachePolicy cachePolicy
Cache maintenance policy derived from query shape.
Definition query_common.h:1198
GAIA_NODISCARD bool calc_can_reuse_dynamic_cache() const
Returns whether the current query shape can reuse dynamic-cache results.
Definition query_common.h:1355
uint8_t groupDepCnt
Number of defined group dependencies.
Definition query_common.h:1175
GAIA_NODISCARD bool grouping_payload_equal(const Data &other) const
Returns true when grouping identity payload matches another query context payload.
Definition query_common.h:1495
GAIA_NODISCARD bool has_match_terms() const
Returns whether any term declares presence-only access.
Definition query_common.h:1208
QueryEntityArray changed
Array of filtered components.
Definition query_common.h:1147
GAIA_NODISCARD bool lookup_keys_equal(const Data &other) const
Returns true when canonical lookup arrays match another query context payload.
Definition query_common.h:1394
GAIA_NODISCARD std::span< const Entity > changed_view() const
Returns changed-filter component ids.
Definition query_common.h:1220
GAIA_NODISCARD QueryLookupHash::Type hash_identity_payload() const
Returns the hash contribution from the full shared query identity payload.
Definition query_common.h:1560
cnt::sarray< QueryTerm, MAX_ITEMS_IN_QUERY > terms
Array of terms.
Definition query_common.h:1135
GAIA_NODISCARD QueryLookupHash::Type hash_grouping_payload() const
Returns the hash contribution from grouping identity payload.
Definition query_common.h:1550
LookupIdentity lookupIdentity
Cold canonical shared-query identity payload.
Definition query_common.h:1204
DirectTargetEvalKind directTargetEvalKind
Specialized direct-target evaluation shape for single-term queries.
Definition query_common.h:1184
QueryArchetypeCacheIndexMap lastMatchedArchetypeIdx_Not
Incremental lookup cursors for NOT selector terms.
Definition query_common.h:1141
bool canReuseDynamicCache
True when a dynamic cache can be reused by checking tracked runtime inputs.
Definition query_common.h:1194
Authored and compiled state defining query identity and execution behavior.
Definition query_common.h:897
GAIA_NODISCARD bool operator!=(const QueryCtx &other) const noexcept
Compares query contexts for distinct shared query identity.
Definition query_common.h:1975
struct gaia::ecs::QueryCtx::Data data
Compiled query payload.
ComponentCache * cc
Component cache.
Definition query_common.h:901
const World * w
World against which the query is compiled and executed.
Definition query_common.h:899
void refresh()
Rebuilds derived masks, dependency metadata, and cache policy after authored terms change.
Definition query_common.h:1584
GAIA_NODISCARD bool operator==(const QueryCtx &other) const noexcept
Compares query contexts during initial shared-query lookup.
Definition query_common.h:1962
QueryFlags
Query maintenance and execution flags derived during compilation.
Definition query_common.h:908
@ HasPrefabTerms
Query explicitly mentions Prefab and therefore bypasses automatic exclusion.
Definition query_common.h:926
@ SortGroups
Cached group ranges require sorting.
Definition query_common.h:914
@ MatchPrefab
Includes prefab entities without requiring an explicit Prefab term.
Definition query_common.h:924
@ Empty
No query flags are set.
Definition query_common.h:910
@ SortEntities
Cached entity slices require sorting.
Definition query_common.h:912
@ OrderGroups
Grouped archetypes are ordered by group identifier during cache refresh.
Definition query_common.h:928
@ Complex
Query requires the general matching path.
Definition query_common.h:916
@ HasPairTerms
At least one query term is a relationship pair.
Definition query_common.h:932
@ HasVariableTerms
Query contains variable-based lookup terms.
Definition query_common.h:922
@ Recompile
VM opcode recompilation is pending.
Definition query_common.h:918
@ HasSourceTerms
Query contains fixed-source lookup terms.
Definition query_common.h:920
@ HasMatchTerms
At least one term matches presence without accessing its payload.
Definition query_common.h:930
DependencyFlags
Dependency facts derived from the compiled term set.
Definition query_common.h:994
@ DependencyHasWildcardTerms
At least one wildcard id or pair term is present.
Definition query_common.h:1008
@ DependencyHasPositiveTerms
At least one positive ALL or OR term is present.
Definition query_common.h:1002
@ DependencyHasSourceTerms
At least one term uses a fixed source entity.
Definition query_common.h:998
@ DependencyHasEntityFilterTerms
At least one term requires per-entity filtering.
Definition query_common.h:1016
@ DependencyHasVariableTerms
At least one term contains a runtime variable.
Definition query_common.h:1000
@ DependencyHasPotentialInheritedIdTerms
A term shape may resolve through inherited-id matching.
Definition query_common.h:1020
@ DependencyHasAnyTerms
At least one optional ANY term is present.
Definition query_common.h:1006
@ DependencyHasInheritedDataTerms
Iteration requires cached inherited component pointers.
Definition query_common.h:1018
@ DependencyHasTraversalTerms
At least one source term traverses a relation.
Definition query_common.h:1014
@ DependencyHasSort
Query has an entity sorting callback.
Definition query_common.h:1010
@ DependencyNone
No dependency facts are present.
Definition query_common.h:996
@ DependencyHasNegativeTerms
At least one negative NOT term is present.
Definition query_common.h:1004
@ DependencyHasGroup
Query has an archetype grouping callback.
Definition query_common.h:1012
QueryIdentity q
Query identity.
Definition query_common.h:905
CreateArchetypeMatchKind
Matcher selected for newly created archetypes.
Definition query_common.h:946
@ DirectStructuralTerms
Evaluates a small immediate ALL, OR, and NOT query directly on the archetype.
@ Vm
Uses the normal one-archetype VM path.
DirectTargetEvalKind
Specialized evaluation shape for concrete target entities.
Definition query_common.h:970
@ SingleAllSemanticIsMixed
Evaluates one required semantic Is term before remaining direct terms.
@ SingleAllDirectMixed
Evaluates one required direct-storage term before remaining direct terms.
@ SingleAllDirectMixedDirect
Evaluates one required direct-storage term before remaining direct-storage terms.
@ SingleAllInheritedMixed
Evaluates one required inherited-data term before remaining direct terms.
@ Generic
Uses the general compiled query evaluator.
@ SingleAllSemanticIs
Evaluates one required semantic Is term.
@ SingleAllInherited
Evaluates one required term through inherited component data.
@ SingleAllInIsMixed
Evaluates one required inherited-inclusive Is term before remaining direct terms.
@ SingleAllInIs
Evaluates one required inherited-inclusive Is term.
@ SingleAllDirect
Evaluates one required direct-storage term.
static GAIA_NODISCARD bool equals_no_handle_assumption(const QueryCtx &leftCtx, const QueryCtx &rightCtx) noexcept
Compares shared query identity without requiring invalid handles.
Definition query_common.h:1949
QueryLookupHash hashLookup
Lookup hash for this query.
Definition query_common.h:903
DynamicCacheKind
Dynamic-cache dependency shape derived from compiled query metadata.
Definition query_common.h:954
@ Variable
Dynamic cache tracks runtime variable bindings only.
@ TraversedSource
Dynamic cache tracks a traversed source closure.
@ DirectSource
Dynamic cache tracks concrete source entity archetype versions.
@ Mixed
Dynamic cache tracks more than one dependency family.
@ None
Query does not use dynamic-cache validation.
@ RelationOnly
Dynamic cache is invalidated by relation version dependencies only.
CachePolicy
Strategy used to maintain cached archetype matches.
Definition query_common.h:936
@ Immediate
Updates a structural query immediately when an archetype is created.
@ Lazy
Refreshes a structural query lazily on the next read.
@ Dynamic
Repairs source- or variable-dependent cached state on demand.
void init(World *pWorld)
Attaches the query context to a world and its component cache.
Definition query_common.h:1578
Hashmap lookup structure used for Entity.
Definition query_common.h:415
QueryHandle handle() const
Returns the represented query handle.
Definition query_common.h:446
bool operator!=(const QueryHandleLookupKey &other) const
Compares lookup keys for inequality.
Definition query_common.h:469
bool operator==(const QueryHandleLookupKey &other) const
Compares lookup keys using their cached hashes and handles.
Definition query_common.h:459
static constexpr bool IsDirectHashKey
Indicates that Gaia containers may consume hash() without rehashing the key.
Definition query_common.h:431
QueryHandleLookupKey(QueryHandle handle)
Constructs a lookup key and precomputes its hash.
Definition query_common.h:436
size_t hash() const
Returns the precomputed container hash.
Definition query_common.h:452
Stable query-slot identifier combining a slot index and generation.
Definition query_common.h:349
static constexpr uint32_t IdMask
Bit mask spanning the query-id portion of a packed handle.
Definition query_common.h:351
GAIA_NODISCARD auto gen() const
Returns the query slot generation.
Definition query_common.h:401
GAIA_NODISCARD auto id() const
Returns the query slot identifier.
Definition query_common.h:396
GAIA_NODISCARD auto value() const
Returns the packed handle representation.
Definition query_common.h:406
GAIA_NODISCARD constexpr bool operator!=(const QueryHandle &other) const noexcept
Compares packed query handles for inequality.
Definition query_common.h:390
QueryHandle(QueryId id, uint32_t gen)
Constructs a handle from query slot metadata.
Definition query_common.h:370
Slot and serialization identity owned by a compiled query context.
Definition query_common.h:877
GAIA_NODISCARD QuerySerBuffer & ser_buffer(World *world)
Returns this query's serialization buffer in a world.
Definition query_common.h:886
QueryHandle handle
Query id.
Definition query_common.h:879
QueryId serId
Serialization id.
Definition query_common.h:881
void ser_buffer_reset(World *world)
Resets this query's serialization buffer in a world.
Definition query_common.h:891
User-provided query input.
Definition query_common.h:478
Entity entSrc
Source entity to query the id on. If id==EntityBad the source is fixed. If id!=src the source is vari...
Definition query_common.h:491
QueryTravKind travKind
Source traversal filter. Self means checking the source itself, Up means checking traversed ancestors...
Definition query_common.h:498
Entity id
Entity/Component/Pair to query.
Definition query_common.h:487
QueryAccess access
Access type.
Definition query_common.h:485
Entity entTrav
Optional traversal relation for source lookups. When set, the lookup starts at src and then walks rel...
Definition query_common.h:494
static constexpr uint8_t TravDepthUnlimited
Traversal-depth value selecting the internally bounded unlimited mode.
Definition query_common.h:480
QueryOpKind op
Operation to perform with the input.
Definition query_common.h:483
QueryMatchKind matchKind
Match semantics for terms with special meaning, such as Pair(Is, X).
Definition query_common.h:503
uint8_t travDepth
Maximum number of traversal steps. 0 means unlimited traversal depth (bounded internally,...
Definition query_common.h:501
Additional options for query terms. This can be used to configure source lookup, traversal and access...
Definition query_common.h:509
QueryTermOptions & trav_self_parent(Entity relation=ChildOf)
Checks the source and its immediate relation target.
Definition query_common.h:569
uint8_t travDepth
Maximum number of traversal steps. 0 means unlimited traversal depth (bounded internally,...
Definition query_common.h:521
QueryTermOptions & write()
Requests mutable access to the term.
Definition query_common.h:648
QueryTermOptions & trav_depth(uint8_t maxDepth)
Sets the maximum traversal distance.
Definition query_common.h:627
Entity entSrc
Source entity to query from.
Definition query_common.h:514
QueryTermOptions & no_access()
Matches an all(), or_(), or any() term without accessing its payload.
Definition query_common.h:634
QueryTermOptions & trav_self_down(Entity relation=ChildOf)
Checks the source and recursively traverses entities targeting it.
Definition query_common.h:589
QueryTermOptions & trav(Entity relation=ChildOf)
Traverses the source and its relation targets without a user depth limit.
Definition query_common.h:539
QueryTermOptions & trav_child(Entity relation=ChildOf)
Checks only immediate entities targeting the source.
Definition query_common.h:599
Entity entTrav
Optional traversal relation used for source lookup.
Definition query_common.h:516
QueryAccess access
Access mode for the term. When None, typed query terms infer read/write access from template mutabili...
Definition query_common.h:524
QueryMatchKind matchKind
Match semantics for terms with special meaning, such as Pair(Is, X).
Definition query_common.h:526
QueryTermOptions & src(Entity source)
Selects a fixed or variable source entity for the term.
Definition query_common.h:531
QueryTermOptions & in()
Allows direct and inherited matches for the term. Same helper name as EntityIdOptions::in().
Definition query_common.h:664
QueryTravKind travKind
Source traversal filter.
Definition query_common.h:518
QueryTermOptions & trav_kind(QueryTravKind kind)
Replaces the traversal filter without changing its relation or depth.
Definition query_common.h:619
static constexpr uint8_t TravDepthUnlimited
Traversal-depth value selecting the internally bounded unlimited mode.
Definition query_common.h:511
QueryTermOptions & read()
Requests read-only access to the term.
Definition query_common.h:641
QueryTermOptions & trav_parent(Entity relation=ChildOf)
Checks only the immediate relation target of the source.
Definition query_common.h:559
QueryTermOptions & trav_down(Entity relation=ChildOf)
Traverses entities targeting the source without a user depth limit.
Definition query_common.h:579
QueryTermOptions & direct()
Restricts the term to direct storage matches. Same helper name as EntityIdOptions::direct().
Definition query_common.h:656
QueryTermOptions & trav_self_child(Entity relation=ChildOf)
Checks the source and immediate entities targeting it.
Definition query_common.h:609
QueryTermOptions & trav_up(Entity relation=ChildOf)
Traverses relation targets without checking the source itself.
Definition query_common.h:549
Internal representation of QueryInput.
Definition query_common.h:742
bool operator==(const QueryTerm &other) const
Compares the matching identity of two compiled terms.
Definition query_common.h:767
Entity id
Queried id.
Definition query_common.h:744
Archetype * srcArchetype
Archetype of the src entity.
Definition query_common.h:756
uint8_t travDepth
Maximum number of traversal steps.
Definition query_common.h:752
QueryMatchKind matchKind
Match semantics for this term.
Definition query_common.h:754
QueryAccess access
Resolved payload access, retained when query identity is canonicalized.
Definition query_common.h:762
bool operator!=(const QueryTerm &other) const
Compares the matching identity of two compiled terms for inequality.
Definition query_common.h:774
uint8_t fieldIndex
Stable execution field index matching the user-defined query field order.
Definition query_common.h:760
Entity entTrav
Optional traversal relation for source lookups.
Definition query_common.h:748
QueryOpKind op
Operation to perform with the term.
Definition query_common.h:758
Entity src
Source of where the queried id is looked up at.
Definition query_common.h:746
QueryTravKind travKind
Source traversal filter.
Definition query_common.h:750
One lookup-key record collected while indexing a single archetype.
Definition query_common.h:326
EntityLookupKey key
Entity or wildcard-pair lookup key represented by this record.
Definition query_common.h:328
ComponentIndexEntry entry
Reverse-index data associated with key.
Definition query_common.h:330
GAIA_NODISCARD bool matches(EntityLookupKey other) const
Tests whether this item represents a lookup key.
Definition query_common.h:335
Functor for sorting terms in a query before compilation.
Definition query_common.h:1981
constexpr bool operator()(const QueryTerm &lhs, const QueryTerm &rhs) const
Orders two terms by their canonical lookup representation.
Definition query_common.h:1986