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_for_each_direct_term_entity(const World& world, Entity term, void* ctx, bool (*func)(void*, Entity));
42 GAIA_NODISCARD bool
43 world_for_each_in_term_entity(const World& world, Entity term, void* ctx, bool (*func)(void*, Entity));
44 GAIA_NODISCARD bool
45 world_for_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
104 };
106 enum class QueryMatchKind : uint8_t {
108 Semantic,
110 In,
112 Direct
113 };
115 enum class QueryInputFlags : uint8_t {
117 None,
119 Variable
120 };
122 enum class QueryTravKind : uint8_t {
124 None = 0x00,
126 Self = 0x01,
128 Up = 0x02,
130 Down = 0x04
131 };
132
133 GAIA_GCC_WARNING_POP()
134
135
139 GAIA_NODISCARD constexpr QueryTravKind operator|(QueryTravKind lhs, QueryTravKind rhs) {
140 return (QueryTravKind)((uint8_t)lhs | (uint8_t)rhs);
141 }
142
147 GAIA_NODISCARD constexpr bool query_trav_has(QueryTravKind value, QueryTravKind bit) {
148 return (((uint8_t)value) & ((uint8_t)bit)) != 0;
149 }
150
152 using QueryLookupHash = core::direct_hash_key<uint64_t>;
154 using QueryEntityArray = cnt::sarray<Entity, MAX_ITEMS_IN_QUERY>;
156 using QuerySerMap = cnt::map<QueryId, QuerySerBuffer>;
157
161 uint32_t index = 0;
163 uint32_t revision = 0;
164 };
165
170
172 static constexpr uint16_t ComponentIndexBad = (uint16_t)-1;
173
179 uint16_t compIdx = ComponentIndexBad;
181 uint16_t matchCount = 0;
182
186 GAIA_NODISCARD bool matches(const Archetype* pOther) const {
187 return pArchetype == pOther;
188 }
189 };
190
194 public:
198 using size_type = uint32_t;
199
200 private:
201 ComponentIndexEntry m_inline{};
203 size_type m_size = 0;
204
205 void collapse_to_inline_if_needed() {
206 if (m_size != 1 || m_items.empty())
207 return;
208
209 m_inline = m_items[0];
210 m_items.clear();
211 }
212
213 public:
214 ComponentIndexEntryArray() = default;
215 ComponentIndexEntryArray(ComponentIndexEntryArray&&) noexcept = default;
216 ComponentIndexEntryArray(const ComponentIndexEntryArray&) = default;
217 ComponentIndexEntryArray& operator=(ComponentIndexEntryArray&&) noexcept = default;
218 ComponentIndexEntryArray& operator=(const ComponentIndexEntryArray&) = default;
219
222 GAIA_NODISCARD bool empty() const noexcept {
223 return m_size == 0;
224 }
225
228 GAIA_NODISCARD size_type size() const noexcept {
229 return m_size;
230 }
231
234 GAIA_NODISCARD ComponentIndexEntry* data() noexcept {
235 return m_size <= 1 ? &m_inline : m_items.data();
236 }
237
240 GAIA_NODISCARD const ComponentIndexEntry* data() const noexcept {
241 return m_size <= 1 ? &m_inline : m_items.data();
242 }
243
246 GAIA_NODISCARD ComponentIndexEntry* begin() noexcept {
247 return data();
248 }
249
252 GAIA_NODISCARD const ComponentIndexEntry* begin() const noexcept {
253 return data();
254 }
255
258 GAIA_NODISCARD ComponentIndexEntry* end() noexcept {
259 return data() + m_size;
260 }
261
264 GAIA_NODISCARD const ComponentIndexEntry* end() const noexcept {
265 return data() + m_size;
266 }
267
271 GAIA_NODISCARD ComponentIndexEntry& operator[](size_type idx) noexcept {
272 GAIA_ASSERT(idx < m_size);
273 return data()[idx];
274 }
275
279 GAIA_NODISCARD const ComponentIndexEntry& operator[](size_type idx) const noexcept {
280 GAIA_ASSERT(idx < m_size);
281 return data()[idx];
282 }
283
286 GAIA_NODISCARD ComponentIndexEntry& back() noexcept {
287 GAIA_ASSERT(m_size > 0);
288 return (*this)[m_size - 1];
289 }
290
293 GAIA_NODISCARD const ComponentIndexEntry& back() const noexcept {
294 GAIA_ASSERT(m_size > 0);
295 return (*this)[m_size - 1];
296 }
297
301 if (m_size == 0) {
302 m_inline = entry;
303 m_size = 1;
304 return;
305 }
306
307 if (m_size == 1) {
308 m_items.push_back(m_inline);
309 m_items.push_back(entry);
310 m_size = 2;
311 return;
312 }
313
314 m_items.push_back(entry);
315 ++m_size;
316 }
317
319 void pop_back() {
320 GAIA_ASSERT(m_size > 0);
321 if (m_size == 1) {
322 m_size = 0;
323 return;
324 }
325
326 m_items.pop_back();
327 --m_size;
328 collapse_to_inline_if_needed();
329 }
330 };
331
335 EntityLookupKey key = EntityBadLookupKey;
338
342 GAIA_NODISCARD bool matches(EntityLookupKey other) const {
343 return key == other;
344 }
345 };
346
349
351 static constexpr QueryId QueryIdBad = (QueryId)-1;
353 static constexpr GroupId GroupIdMax = ((GroupId)-1) - 1;
354
356 struct QueryHandle {
358 static constexpr uint32_t IdMask = QueryIdBad;
359
360 private:
361 struct HandleData {
362 QueryId id;
363 uint32_t gen;
364 };
365
366 union {
367 HandleData data;
368 uint64_t val;
369 };
370
371 public:
372 constexpr QueryHandle() noexcept: val((uint64_t)-1) {};
373
377 QueryHandle(QueryId id, uint32_t gen) {
378 data.id = id;
379 data.gen = gen;
380 }
381 ~QueryHandle() = default;
382
383 QueryHandle(QueryHandle&&) noexcept = default;
384 QueryHandle(const QueryHandle&) = default;
385 QueryHandle& operator=(QueryHandle&&) noexcept = default;
386 QueryHandle& operator=(const QueryHandle&) = default;
387
391 GAIA_NODISCARD constexpr bool operator==(const QueryHandle& other) const noexcept {
392 return val == other.val;
393 }
397 GAIA_NODISCARD constexpr bool operator!=(const QueryHandle& other) const noexcept {
398 return val != other.val;
399 }
400
403 GAIA_NODISCARD auto id() const {
404 return data.id;
405 }
408 GAIA_NODISCARD auto gen() const {
409 return data.gen;
410 }
413 GAIA_NODISCARD auto value() const {
414 return val;
415 }
416 };
417
419 inline static const QueryHandle QueryHandleBad = QueryHandle();
420
425
426 private:
428 QueryHandle m_handle;
430 LookupHash m_hash;
431
432 static LookupHash calc(QueryHandle handle) {
433 return {core::calculate_hash64(handle.value())};
434 }
435
436 public:
438 static constexpr bool IsDirectHashKey = true;
439
440 QueryHandleLookupKey() = default;
443 explicit QueryHandleLookupKey(QueryHandle handle): m_handle(handle), m_hash(calc(handle)) {}
444 ~QueryHandleLookupKey() = default;
445
448 QueryHandleLookupKey& operator=(const QueryHandleLookupKey&) = default;
449 QueryHandleLookupKey& operator=(QueryHandleLookupKey&&) = default;
450
454 return m_handle;
455 }
456
459 size_t hash() const {
460 return (size_t)m_hash.hash;
461 }
462
466 bool operator==(const QueryHandleLookupKey& other) const {
467 if GAIA_LIKELY (m_hash != other.m_hash)
468 return false;
469
470 return m_handle == other.m_handle;
471 }
472
476 bool operator!=(const QueryHandleLookupKey& other) const {
477 return !operator==(other);
478 }
479 };
480
482 inline static const QueryHandleLookupKey QueryHandleBadLookupKey = QueryHandleLookupKey(QueryHandleBad);
483
485 struct QueryInput {
487 static constexpr uint8_t TravDepthUnlimited = 0;
488
490 QueryOpKind op = QueryOpKind::All;
492 QueryAccess access = QueryAccess::Read;
498 Entity entSrc = EntityBad;
501 Entity entTrav = EntityBad;
505 QueryTravKind travKind = QueryTravKind::Self | QueryTravKind::Up;
510 QueryMatchKind matchKind = QueryMatchKind::Semantic;
511 };
512
519
521 Entity entSrc = EntityBad;
523 Entity entTrav = EntityBad;
525 QueryTravKind travKind = QueryTravKind::Self | QueryTravKind::Up;
531 QueryAccess access = QueryAccess::None;
533 QueryMatchKind matchKind = QueryMatchKind::Semantic;
534
539 entSrc = source;
540 return *this;
541 }
542
546 QueryTermOptions& trav(Entity relation = ChildOf) {
547 entTrav = relation;
548 travKind = QueryTravKind::Self | QueryTravKind::Up;
550 return *this;
551 }
552
556 QueryTermOptions& trav_up(Entity relation = ChildOf) {
557 entTrav = relation;
558 travKind = QueryTravKind::Up;
560 return *this;
561 }
562
566 QueryTermOptions& trav_parent(Entity relation = ChildOf) {
567 entTrav = relation;
568 travKind = QueryTravKind::Up;
569 travDepth = 1;
570 return *this;
571 }
572
577 entTrav = relation;
578 travKind = QueryTravKind::Self | QueryTravKind::Up;
579 travDepth = 1;
580 return *this;
581 }
582
586 QueryTermOptions& trav_down(Entity relation = ChildOf) {
587 entTrav = relation;
588 travKind = QueryTravKind::Down;
590 return *this;
591 }
592
597 entTrav = relation;
598 travKind = QueryTravKind::Self | QueryTravKind::Down;
600 return *this;
601 }
602
606 QueryTermOptions& trav_child(Entity relation = ChildOf) {
607 entTrav = relation;
608 travKind = QueryTravKind::Down;
609 travDepth = 1;
610 return *this;
611 }
612
617 entTrav = relation;
618 travKind = QueryTravKind::Self | QueryTravKind::Down;
619 travDepth = 1;
620 return *this;
621 }
622
626 QueryTermOptions& trav_kind(QueryTravKind kind) {
627 travKind = kind;
628 return *this;
629 }
630
634 QueryTermOptions& trav_depth(uint8_t maxDepth) {
635 travDepth = maxDepth;
636 return *this;
637 }
638
642 access = QueryAccess::Read;
643 return *this;
644 }
645
649 access = QueryAccess::Write;
650 return *this;
651 }
652
656 matchKind = QueryMatchKind::Direct;
657 return *this;
658 }
659
663 matchKind = QueryMatchKind::In;
664 return *this;
665 }
666 };
667
681 uint8_t readCnt = 0;
683 uint8_t writeCnt = 0;
684
687 GAIA_NODISCARD std::span<const Entity> reads_view() const {
688 return {reads.data(), readCnt};
689 }
690
693 GAIA_NODISCARD std::span<const Entity> writes_view() const {
694 return {writes.data(), writeCnt};
695 }
696
699 void add_read(Entity entity) {
700 if (entity == EntityBad || core::has(reads_view(), entity))
701 return;
702
703 GAIA_ASSERT(readCnt < MAX_ITEMS_IN_QUERY);
704 if (readCnt < MAX_ITEMS_IN_QUERY)
705 reads[readCnt++] = entity;
706 }
707
710 void add_write(Entity entity) {
711 if (entity == EntityBad || core::has(writes_view(), entity))
712 return;
713
714 GAIA_ASSERT(writeCnt < MAX_ITEMS_IN_QUERY);
715 if (writeCnt < MAX_ITEMS_IN_QUERY)
716 writes[writeCnt++] = entity;
717 }
718
722 GAIA_NODISCARD QueryAccess access(Entity entity) const {
723 if (core::has(writes_view(), entity))
724 return QueryAccess::Write;
725 if (core::has(reads_view(), entity))
726 return QueryAccess::Read;
727 return QueryAccess::None;
728 }
729 };
730
732 struct QueryTerm {
740 QueryTravKind travKind;
742 uint8_t travDepth;
744 QueryMatchKind matchKind;
748 QueryOpKind op;
750 uint8_t fieldIndex = 0;
751
755 bool operator==(const QueryTerm& other) const {
756 return id == other.id && src == other.src && entTrav == other.entTrav && travKind == other.travKind &&
757 travDepth == other.travDepth && matchKind == other.matchKind && op == other.op;
758 }
762 bool operator!=(const QueryTerm& other) const {
763 return !operator==(other);
764 }
765 };
766
771 constexpr bool query_term_less_for_lookup(const QueryTerm& lhs, const QueryTerm& rhs) {
772 if (lhs.op != rhs.op)
773 return lhs.op < rhs.op;
774
775 if (lhs.id != rhs.id)
776 return SortComponentCond()(lhs.id, rhs.id);
777
778 if (lhs.src != rhs.src)
779 return SortComponentCond()(lhs.src, rhs.src);
780
781 if (lhs.entTrav != rhs.entTrav)
782 return SortComponentCond()(lhs.entTrav, rhs.entTrav);
783
784 if (lhs.travKind != rhs.travKind)
785 return (uint8_t)lhs.travKind < (uint8_t)rhs.travKind;
786
787 if (lhs.travDepth != rhs.travDepth)
788 return lhs.travDepth < rhs.travDepth;
789
790 return (uint8_t)lhs.matchKind < (uint8_t)rhs.matchKind;
791 }
792
795 inline void canonicalize_lookup_terms(std::span<QueryTerm> terms) {
796 const auto idsCnt = (uint32_t)terms.size();
797 if (idsCnt > 0) {
798 uint32_t orCnt = 0;
799 uint32_t orIdx = BadIndex;
800 GAIA_FOR(idsCnt) {
801 if (terms[i].op != QueryOpKind::Or)
802 continue;
803 ++orCnt;
804 orIdx = i;
805 if (orCnt > 1)
806 break;
807 }
808
809 if (orCnt == 1)
810 terms[orIdx].op = QueryOpKind::All;
811 }
812
813 core::sort(terms.data(), terms.data() + idsCnt, [](const QueryTerm& left, const QueryTerm& right) {
814 return query_term_less_for_lookup(left, right);
815 });
816 }
817
820 inline void canonicalize_lookup_changed(std::span<Entity> changed) {
821 const auto changedCnt = (uint32_t)changed.size();
822 if (changedCnt > 1)
823 core::sort(changed.data(), changed.data() + changedCnt, SortComponentCond{});
824 }
825
829 GAIA_NODISCARD inline bool term_has_variables(const QueryTerm& term) {
830 if (is_variable(term.src))
831 return true;
832
833 if (term.id.pair())
834 return is_variable(EntityId(term.id.id())) || is_variable(EntityId(term.id.gen()));
835
836 return is_variable(EntityId(term.id.id()));
837 }
838
842 GAIA_NODISCARD inline bool query_term_maps_to_current_archetype(const QueryTerm& term) {
843 return term.src == EntityBad && term.entTrav == EntityBad && !term_has_variables(term);
844 }
845
850 GAIA_NODISCARD inline bool query_term_uses_potential_inherited_id_matching(const QueryTerm& term) {
851 const auto id = term.id;
852 return term.matchKind == QueryMatchKind::Semantic && term.src == EntityBad && term.entTrav == EntityBad &&
853 !term_has_variables(term) && !is_wildcard(id) && !is_variable((EntityId)id.id()) &&
854 (!id.pair() || !is_variable((EntityId)id.gen()));
855 }
856
858 using QueryTermArray = cnt::sarray_ext<QueryTerm, MAX_ITEMS_IN_QUERY>;
860 using QueryTermSpan = std::span<QueryTerm>;
862 using QueryRemappingArray = cnt::sarray_ext<uint8_t, MAX_ITEMS_IN_QUERY>;
863
869 QueryId serId = QueryIdBad;
870
874 GAIA_NODISCARD QuerySerBuffer& ser_buffer(World* world) {
875 return query_buffer(*world, serId);
876 }
879 void ser_buffer_reset(World* world) {
880 query_buffer_reset(*world, serId);
881 }
882 };
883
885 struct QueryCtx {
887 const World* w{};
889 ComponentCache* cc{};
894
896 enum QueryFlags : uint16_t {
898 Empty = 0x00,
904 Complex = 0x04,
906 Recompile = 0x08,
916 OrderGroups = 0x100,
917 };
918
920 enum class CachePolicy : uint8_t {
922 Immediate,
924 Lazy,
926 Dynamic,
927 };
928
930 enum class CreateArchetypeMatchKind : uint8_t {
932 Vm,
935 };
936
938 enum class DynamicCacheKind : uint8_t {
940 None,
948 Variable,
950 Mixed
951 };
952
966
996
998 struct Data {
1012 uint8_t exclusionCnt = 0;
1014 uint8_t relationCnt = 0;
1016 uint8_t sourceEntityCnt = 0;
1018 uint8_t sourceTermCnt = 0;
1021
1023 void clear() {
1025 exclusionCnt = 0;
1026 relationCnt = 0;
1027 sourceEntityCnt = 0;
1028 sourceTermCnt = 0;
1030 }
1031
1034 GAIA_NODISCARD std::span<const Entity> create_selectors_view() const {
1036 }
1037
1040 GAIA_NODISCARD std::span<const Entity> exclusions_view() const {
1041 return {exclusions.data(), exclusionCnt};
1042 }
1043
1046 GAIA_NODISCARD std::span<const Entity> relations_view() const {
1047 return {relations.data(), relationCnt};
1048 }
1049
1052 GAIA_NODISCARD std::span<const Entity> src_entities_view() const {
1054 }
1055
1059 flags = (DependencyFlags)(flags | dependency);
1060 }
1061
1065 GAIA_NODISCARD bool has_dep_flag(DependencyFlags dependency) const {
1066 return (flags & dependency) != 0;
1067 }
1068
1071 void add_rel(Entity relation) {
1072 if (relation == EntityBad || core::has(relations_view(), relation))
1073 return;
1074
1075 GAIA_ASSERT(relationCnt < MAX_ITEMS_IN_QUERY);
1076 relations[relationCnt++] = relation;
1077 }
1078
1081 void add_src_entity(Entity entity) {
1082 if (entity == EntityBad || core::has(src_entities_view(), entity))
1083 return;
1084
1085 GAIA_ASSERT(sourceEntityCnt < MAX_ITEMS_IN_QUERY);
1086 sourceEntities[sourceEntityCnt++] = entity;
1087 }
1088
1091 GAIA_NODISCARD bool can_reuse_src_cache() const {
1093 }
1094 };
1095
1105
1117 uint8_t idsCnt = 0;
1119 uint8_t changedCnt = 0;
1129 TSortByFunc sortByFunc;
1133 TGroupByFunc groupByFunc;
1135 QueryMask queryMask;
1138 uint32_t as_mask_0;
1141 uint32_t as_mask_1;
1143 uint8_t firstNot;
1145 uint8_t firstAny;
1147 uint8_t firstOr;
1149 uint8_t groupDepCnt = 0;
1154 uint16_t flags;
1156 uint16_t cacheSrcTrav = 0;
1179
1182 GAIA_NODISCARD std::span<const Entity> ids_view() const {
1183 return {ids.data(), idsCnt};
1184 }
1185
1188 GAIA_NODISCARD std::span<const Entity> changed_view() const {
1189 return {changed.data(), changedCnt};
1190 }
1191
1194 GAIA_NODISCARD std::span<const uint8_t> changed_fields_view() const {
1195 return {changedFields.data(), changedCnt};
1196 }
1197
1200 GAIA_NODISCARD std::span<const Entity> group_deps_view() const {
1201 return {groupDeps.data(), groupDepCnt};
1202 }
1203
1206 GAIA_NODISCARD std::span<const QueryTerm> lookup_terms_view() const {
1208 }
1209
1212 GAIA_NODISCARD std::span<QueryTerm> lookup_terms_view_mut() {
1214 }
1215
1218 GAIA_NODISCARD std::span<const Entity> changed_lookup_view() const {
1220 }
1221
1224 GAIA_NODISCARD std::span<Entity> changed_lookup_view_mut() {
1226 }
1227
1230 GAIA_NODISCARD std::span<const Entity> group_deps_lookup_view() const {
1232 }
1233
1236 GAIA_NODISCARD std::span<Entity> group_deps_lookup_view_mut() {
1238 }
1239
1242 void add_group_dep(Entity relation) {
1243 if (relation == EntityBad || core::has(group_deps_view(), relation))
1244 return;
1245
1246 GAIA_ASSERT(groupDepCnt < MAX_ITEMS_IN_QUERY);
1247 if (groupDepCnt < MAX_ITEMS_IN_QUERY)
1248 groupDeps[groupDepCnt++] = relation;
1249 }
1250
1254
1255 const bool hasBuiltInGroupDep = groupBy != EntityBad && (groupByFunc == group_by_func_default ||
1256 groupByFunc == group_by_func_depth_order);
1257 if (hasBuiltInGroupDep)
1259 for (auto relation: group_deps_view())
1260 deps.add_rel(relation);
1261 }
1262
1265 GAIA_NODISCARD std::span<QueryTerm> terms_view_mut() {
1266 return {terms.data(), idsCnt};
1267 }
1270 GAIA_NODISCARD std::span<const QueryTerm> terms_view() const {
1271 return {terms.data(), idsCnt};
1272 }
1273
1279
1280 const bool hasVariables = deps.has_dep_flag(DependencyHasVariableTerms);
1281 const bool hasSources = deps.has_dep_flag(DependencyHasSourceTerms);
1282 const bool hasTraversal = deps.has_dep_flag(DependencyHasTraversalTerms);
1283 const bool hasRelations = deps.relationCnt != 0;
1284 if (hasSources && hasTraversal && !hasVariables)
1286
1287 const uint8_t depCnt = (uint8_t)hasVariables + (uint8_t)hasSources + (uint8_t)hasRelations;
1288 if (depCnt > 1)
1290 if (hasVariables)
1292 if (hasSources)
1294 if (hasRelations)
1297 }
1298
1304
1310
1313 GAIA_NODISCARD bool calc_can_reuse_dynamic_cache() const {
1315 return false;
1316
1317 switch (dynamicCacheKind) {
1319 return false;
1322 return true;
1324 return deps.can_reuse_src_cache();
1326 return deps.can_reuse_src_cache() && cacheSrcTrav != 0;
1329 return true;
1331 return false;
1333 }
1334
1335 GAIA_ASSERT(false);
1336 return false;
1337 }
1338
1341 GAIA_FOR(idsCnt) lookupIdentity.lookupTerms[i] = terms[i];
1342 canonicalize_lookup_terms(lookup_terms_view_mut());
1343 GAIA_FOR(changedCnt) lookupIdentity.changed[i] = changed[i];
1344 canonicalize_lookup_changed(changed_lookup_view_mut());
1346 canonicalize_lookup_changed(group_deps_lookup_view_mut());
1347 }
1348
1352 GAIA_NODISCARD bool lookup_keys_equal(const Data& other) const {
1353 {
1354 const auto left = lookup_terms_view();
1355 const auto right = other.lookup_terms_view();
1356 GAIA_FOR((uint32_t)left.size()) {
1357 if (left[i] != right[i])
1358 return false;
1359 }
1360 }
1361
1362 {
1363 const auto left = changed_lookup_view();
1364 const auto right = other.changed_lookup_view();
1365 GAIA_FOR((uint32_t)left.size()) {
1366 if (left[i] != right[i])
1367 return false;
1368 }
1369 }
1370
1371 {
1372 const auto left = group_deps_lookup_view();
1373 const auto right = other.group_deps_lookup_view();
1374 GAIA_FOR((uint32_t)left.size()) {
1375 if (left[i] != right[i])
1376 return false;
1377 }
1378 }
1379
1380 return true;
1381 }
1382
1387
1388 // Ids & ops
1389 {
1390 QueryLookupHash::Type hash = 0;
1391
1392 for (const auto& pair: lookup_terms_view()) {
1393 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.op);
1394 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.id.value());
1395 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.src.value());
1396 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.entTrav.value());
1397 hash = core::hash_combine(hash, (QueryLookupHash::Type)(uint8_t)pair.travKind);
1398 hash = core::hash_combine(hash, (QueryLookupHash::Type)pair.travDepth);
1399 hash = core::hash_combine(hash, (QueryLookupHash::Type)(uint8_t)pair.matchKind);
1400 }
1401 hash = core::hash_combine(hash, (QueryLookupHash::Type)idsCnt);
1402 hash = core::hash_combine(hash, (QueryLookupHash::Type)readWriteMask);
1403 hash = core::hash_combine(hash, (QueryLookupHash::Type)cacheSrcTrav);
1404
1405 const bool matchPrefab = (flags & QueryFlags::MatchPrefab) != 0;
1406 hash = core::hash_combine(hash, (QueryLookupHash::Type)matchPrefab);
1407
1408 hashLookup = hash;
1409 }
1410
1411 // Filters
1412 {
1413 QueryLookupHash::Type hash = 0;
1414
1415 for (const auto entity: changed_lookup_view())
1416 hash = core::hash_combine(hash, (QueryLookupHash::Type)entity.value());
1417 hash = core::hash_combine(hash, (QueryLookupHash::Type)changedCnt);
1418
1419 hashLookup = core::hash_combine(hashLookup, hash);
1420 }
1421
1422 // Explicit grouping dependencies
1423 {
1424 QueryLookupHash::Type hash = 0;
1425
1426 for (const auto entity: group_deps_lookup_view())
1427 hash = core::hash_combine(hash, (QueryLookupHash::Type)entity.value());
1428 hash = core::hash_combine(hash, (QueryLookupHash::Type)groupDepCnt);
1429
1430 hashLookup = core::hash_combine(hashLookup, hash);
1431 }
1432
1433 return hashLookup;
1434 }
1435
1439 GAIA_NODISCARD bool grouping_payload_equal(const Data& other) const {
1440 if (groupBy != other.groupBy)
1441 return false;
1442 if (groupByFunc != other.groupByFunc)
1443 return false;
1445 }
1446
1450 GAIA_NODISCARD bool sort_payload_equal(const Data& other) const {
1451 return sortBy == other.sortBy && sortByFunc == other.sortByFunc;
1452 }
1453
1456 GAIA_NODISCARD bool has_sort_payload() const {
1457 return sortBy != EntityBad || sortByFunc != nullptr;
1458 }
1459
1463 QueryLookupHash::Type hash = 0;
1464 hash = core::hash_combine(hash, (QueryLookupHash::Type)sortBy.value());
1465 hash = core::hash_combine(hash, (QueryLookupHash::Type)sortByFunc);
1466 return hash;
1467 }
1468
1472 GAIA_NODISCARD bool identity_payload_equal(const Data& other) const {
1473 if (idsCnt != other.idsCnt)
1474 return false;
1475 if (changedCnt != other.changedCnt)
1476 return false;
1477 if (groupDepCnt != other.groupDepCnt)
1478 return false;
1479 if (readWriteMask != other.readWriteMask)
1480 return false;
1481 if (cacheSrcTrav != other.cacheSrcTrav)
1482 return false;
1483 if (!lookup_keys_equal(other))
1484 return false;
1485 if (!sort_payload_equal(other))
1486 return false;
1487 return grouping_payload_equal(other);
1488 }
1489
1493 QueryLookupHash::Type hash = 0;
1494 hash = core::hash_combine(hash, (QueryLookupHash::Type)groupBy.value());
1495 hash = core::hash_combine(hash, (QueryLookupHash::Type)groupByFunc);
1496 hash = core::hash_combine(hash, (QueryLookupHash::Type)((flags & QueryFlags::OrderGroups) != 0));
1497 return hash;
1498 }
1499
1504 if (has_sort_payload())
1505 hash = core::hash_combine(hash, hash_sort_payload());
1506 return core::hash_combine(hash, hash_grouping_payload());
1507 }
1508
1511 GAIA_NODISCARD QueryLookupHash calc_lookup_hash() const {
1512 return {core::calculate_hash64(hash_identity_payload())};
1513 }
1514 } data{};
1515 // Make sure that MAX_ITEMS_IN_QUERY can fit into data.readWriteMask
1516 static_assert(MAX_ITEMS_IN_QUERY < 16);
1517
1520 void init(World* pWorld) {
1521 w = pWorld;
1522 cc = &comp_cache_mut(*pWorld);
1523 }
1524
1526 void refresh() {
1527 const auto mask0_old = data.as_mask_0;
1528 const auto mask1_old = data.as_mask_1;
1529 const auto isComplex_old = data.flags & QueryFlags::Complex;
1530 const auto hasSourceTerms_old = data.flags & QueryFlags::HasSourceTerms;
1531 const auto hasVariableTerms_old = data.flags & QueryFlags::HasVariableTerms;
1532 const auto cachePolicy_old = data.cachePolicy;
1533 const auto createArchetypeMatchKind_old = data.createArchetypeMatchKind;
1534 const auto dynamicCacheKind_old = data.dynamicCacheKind;
1535 const auto canDirectTargetEval_old = data.canDirectTargetEval;
1536 const auto canDirectEntitySeedEvalShape_old = data.canDirectEntitySeedEvalShape;
1537 const auto hasOnlyDirectOrTerms_old = data.hasOnlyDirectOrTerms;
1538 const auto canReuseDynamicCache_old = data.canReuseDynamicCache;
1539 const auto dependencyFlags_old = data.deps.flags;
1540 const auto createSelectorCnt_old = data.deps.createSelectorCnt;
1541 const auto exclusionCnt_old = data.deps.exclusionCnt;
1542 const auto relationCnt_old = data.deps.relationCnt;
1543 const auto sourceEntityCnt_old = data.deps.sourceEntityCnt;
1544 const auto sourceTermCnt_old = data.deps.sourceTermCnt;
1545 auto createSelectors_old = data.deps.createSelectors;
1546 auto exclusions_old = data.deps.exclusions;
1547 auto relations_old = data.deps.relations;
1548 auto sourceEntities_old = data.deps.sourceEntities;
1549
1550 // Update masks
1551 {
1552 uint32_t as_mask_0 = 0;
1553 uint32_t as_mask_1 = 0;
1554 bool isComplex = false;
1555 bool hasSourceTerms = false;
1556 bool hasVariableTerms = false;
1557 bool hasPrefabTerms = false;
1558 bool hasCreateSelector = false;
1559 bool canDirectCreateArchetypeMatch = true;
1560 bool hasEntityFilterTerms = false;
1561 bool canDirectTargetEval = true;
1562 bool hasOnlyDirectOrTerms = true;
1563 bool hasOrTerms = false;
1564 bool hasDirectTargetEvalPositiveTerms = false;
1565 const QueryTerm* pSingleDirectTargetAllTerm = nullptr;
1566 bool singleDirectTargetEvalPossible = true;
1567 QueryEntityArray idsNoSrc;
1568 QueryEntityArray createSelectorsAll;
1569 QueryEntityArray createSelectorsOr;
1570 uint32_t idsNoSrcCnt = 0;
1571 uint8_t createSelectorAllCnt = 0;
1572 uint8_t createSelectorOrCnt = 0;
1573 data.deps.clear();
1575 data.directTargetEvalId = EntityBad;
1576 if (data.sortByFunc != nullptr)
1578 if (data.groupBy != EntityBad)
1580
1581 auto terms = data.terms_view();
1582 const auto cnt = (uint32_t)terms.size();
1583 GAIA_FOR(cnt) {
1584 const auto& term = terms[i];
1585 const auto id = term.id;
1586 hasPrefabTerms |= id == Prefab;
1587 const bool isDirectIsTerm = term.src == EntityBad && term.entTrav == EntityBad &&
1588 !term_has_variables(term) && term.matchKind != QueryMatchKind::Direct &&
1589 id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()) &&
1590 !is_variable((EntityId)id.gen());
1591 const bool isPotentialInheritedTerm = query_term_uses_potential_inherited_id_matching(term);
1592 const bool isInheritedTerm = isPotentialInheritedTerm && world_term_uses_inherit_policy(*w, id);
1593 const bool isNonFragmentingTerm =
1594 term.src == EntityBad && term.entTrav == EntityBad && !term_has_variables(term) &&
1595 ((id.pair() && world_relation_uses_non_fragmenting_storage(*w, pair_rel(*w, id))) ||
1596 (!id.pair() && world_component_is_non_fragmenting(*w, id)));
1597 hasEntityFilterTerms |= isNonFragmentingTerm || isDirectIsTerm || isInheritedTerm;
1598 }
1599
1600 GAIA_FOR(cnt) {
1601 const auto& term = terms[i];
1602 const auto id = term.id;
1603 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term)) {
1604 singleDirectTargetEvalPossible = false;
1605 canDirectTargetEval = false;
1606 hasOnlyDirectOrTerms = false;
1607 }
1608 switch (term.op) {
1609 case QueryOpKind::All:
1610 hasDirectTargetEvalPositiveTerms = true;
1611 if (pSingleDirectTargetAllTerm == nullptr)
1612 pSingleDirectTargetAllTerm = &term;
1613 else
1614 singleDirectTargetEvalPossible = false;
1615 hasOnlyDirectOrTerms = false;
1616 break;
1617 case QueryOpKind::Or:
1618 hasDirectTargetEvalPositiveTerms = true;
1619 hasOrTerms = true;
1620 break;
1621 case QueryOpKind::Not:
1622 break;
1623 case QueryOpKind::Any:
1624 case QueryOpKind::Count:
1625 singleDirectTargetEvalPossible = false;
1626 canDirectTargetEval = false;
1627 hasOnlyDirectOrTerms = false;
1628 break;
1629 }
1630 const bool isDirectIsTerm = term.src == EntityBad && term.entTrav == EntityBad &&
1631 !term_has_variables(term) && term.matchKind != QueryMatchKind::Direct &&
1632 id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()) &&
1633 !is_variable((EntityId)id.gen());
1634 const bool isPotentialInheritedTerm = query_term_uses_potential_inherited_id_matching(term);
1635 const bool isInheritedTerm = isPotentialInheritedTerm && world_term_uses_inherit_policy(*w, id);
1636 const bool isCachedInheritedDataTerm = isInheritedTerm && !world_component_uses_sparse_storage(*w, id);
1637 const bool isNonFragmentingTerm =
1638 term.src == EntityBad && term.entTrav == EntityBad && !term_has_variables(term) &&
1639 ((id.pair() && world_relation_uses_non_fragmenting_storage(*w, pair_rel(*w, id))) ||
1640 (!id.pair() && world_component_is_non_fragmenting(*w, id)));
1641 canDirectCreateArchetypeMatch &= term.src == EntityBad;
1642 if (id.pair() && (is_wildcard(id.id()) || is_wildcard(id.gen())))
1644 const bool hasDynamicRelationUsage =
1645 term.entTrav != EntityBad || term.src != EntityBad || term_has_variables(term);
1646 if (id.pair() && hasDynamicRelationUsage && !is_wildcard(id.id()) && !is_variable((EntityId)id.id()))
1647 data.deps.add_rel(pair_rel(*w, id));
1648 if (term.entTrav != EntityBad) {
1649 data.deps.add_rel(term.entTrav);
1651 }
1652 if (term.src != EntityBad) {
1653 hasSourceTerms = true;
1656 if (!is_variable(term.src))
1657 data.deps.add_src_entity(term.src);
1658 }
1659
1660 if (term_has_variables(term)) {
1661 hasVariableTerms = true;
1663 isComplex = true;
1664 continue;
1665 }
1666
1667 if (isPotentialInheritedTerm)
1669
1670 if (isNonFragmentingTerm || isDirectIsTerm || isInheritedTerm) {
1672 if (isCachedInheritedDataTerm)
1674 if (id.pair() && !is_wildcard(id.id()) && !is_variable((EntityId)id.id()))
1675 data.deps.add_rel(pair_rel(*w, id));
1676 continue;
1677 }
1678
1679 if (hasEntityFilterTerms && term.op == QueryOpKind::Or) {
1680 isComplex = true;
1681 continue;
1682 }
1683
1684 // Source terms are evaluated separately by the VM.
1685 // They should not affect archetype-level query masks.
1686 if (term.src != EntityBad) {
1687 continue;
1688 }
1689
1690 // ANY terms are not hard requirements and must not affect archetype prefilter masks.
1691 if (term.op != QueryOpKind::Any)
1692 idsNoSrc[idsNoSrcCnt++] = id;
1693
1694 if (term.op == QueryOpKind::All || term.op == QueryOpKind::Or) {
1695 hasCreateSelector = true;
1697 if (term.op == QueryOpKind::All)
1698 createSelectorsAll[createSelectorAllCnt++] = id;
1699 else
1700 createSelectorsOr[createSelectorOrCnt++] = id;
1701 } else if (term.op == QueryOpKind::Not) {
1704 } else if (term.op == QueryOpKind::Any) {
1706 }
1707
1708 // Build the Is mask.
1709 // We will use it to identify entities with an Is relationship quickly.
1710 const bool allowSemanticIs = !(
1711 term.matchKind == QueryMatchKind::Direct && id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()));
1712 if (!id.pair()) {
1713 const auto j = (uint32_t)i;
1714 const auto has_as = allowSemanticIs ? (uint32_t)is_base(*w, id) : 0U;
1715 as_mask_0 |= (has_as << j);
1716 } else {
1717 const bool idIsWildcard = is_wildcard(id.id());
1718 const bool isGenWildcard = is_wildcard(id.gen());
1719 isComplex |= (idIsWildcard || isGenWildcard);
1720
1721 if (!idIsWildcard) {
1722 const auto j = (uint32_t)i;
1723 const auto e = pair_rel(*w, id);
1724 const auto has_as = allowSemanticIs ? (uint32_t)is_base(*w, e) : 0U;
1725 as_mask_0 |= (has_as << j);
1726 }
1727
1728 if (!isGenWildcard) {
1729 const auto j = (uint32_t)i;
1730 const auto e = pair_tgt(*w, id);
1731 const auto has_as = allowSemanticIs ? (uint32_t)is_base(*w, e) : 0U;
1732 as_mask_1 |= (has_as << j);
1733 }
1734 }
1735 }
1736
1737 if (singleDirectTargetEvalPossible && pSingleDirectTargetAllTerm != nullptr) {
1738 const auto& term = *pSingleDirectTargetAllTerm;
1739 const auto id = term.id;
1740 if (term.matchKind == QueryMatchKind::In && id.pair() && id.id() == Is.id() && !is_wildcard(id.gen()) &&
1741 !is_variable((EntityId)id.gen())) {
1743 } else if (
1744 term.matchKind == QueryMatchKind::Semantic && id.pair() && id.id() == Is.id() &&
1745 !is_wildcard(id.gen()) && !is_variable((EntityId)id.gen())) {
1747 } else if (
1748 term.matchKind == QueryMatchKind::Semantic && !is_wildcard(id) && !is_variable((EntityId)id.id()) &&
1749 (!id.pair() || !is_variable((EntityId)id.gen())) && world_term_uses_inherit_policy(*w, id)) {
1751 } else {
1753 }
1755 }
1756 data.canDirectTargetEval = canDirectTargetEval && hasDirectTargetEvalPositiveTerms;
1758 data.canDirectTargetEval && data.sortByFunc == nullptr && data.groupBy == EntityBad;
1759 data.hasOnlyDirectOrTerms = hasOnlyDirectOrTerms && hasOrTerms;
1760
1761 // Update the mask
1762 data.as_mask_0 = as_mask_0;
1763 data.as_mask_1 = as_mask_1;
1765 if (createSelectorAllCnt != 0) {
1766 auto selector_rank = [](Entity term) {
1767 if (!term.pair())
1768 return 2;
1769 if (!is_wildcard(term.id()) && !is_wildcard(term.gen()))
1770 return 0;
1771 if (is_wildcard(term.id()) && is_wildcard(term.gen()))
1772 return 3;
1773 return 1;
1774 };
1775
1776 // For immediate structural queries, we choose one required ALL selector as the create-time wake-up key.
1777 // This choice is ordered by:
1778 // 1) smaller component index bucket size first
1779 // 2) if equal, more specific selector first
1780 uint8_t bestIdx = 0;
1781 auto bestBucketSize = world_component_index_bucket_size(*w, createSelectorsAll[0]);
1782 auto bestRank = selector_rank(createSelectorsAll[0]);
1783 GAIA_FOR2_(1, createSelectorAllCnt, i) {
1784 const auto bucketSize = world_component_index_bucket_size(*w, createSelectorsAll[i]);
1785 const auto rank = selector_rank(createSelectorsAll[i]);
1786 if (bucketSize < bestBucketSize || (bucketSize == bestBucketSize && rank < bestRank)) {
1787 bestBucketSize = bucketSize;
1788 bestRank = rank;
1789 bestIdx = (uint8_t)i;
1790 }
1791 }
1792 data.deps.createSelectors[data.deps.createSelectorCnt++] = createSelectorsAll[bestIdx];
1793 } else {
1794 GAIA_FOR(createSelectorOrCnt) {
1795 data.deps.createSelectors[data.deps.createSelectorCnt++] = createSelectorsOr[i];
1796 }
1797 }
1798 if (hasPrefabTerms)
1800 else
1802
1803 if (hasSourceTerms)
1805 else
1807
1808 if (hasVariableTerms)
1810 else
1812
1813 if (hasSourceTerms || hasVariableTerms)
1815 else if (
1816 !hasEntityFilterTerms && data.sortByFunc == nullptr && data.groupBy == EntityBad && hasCreateSelector)
1818 else
1820
1821 data.createArchetypeMatchKind = data.cachePolicy == CachePolicy::Immediate && canDirectCreateArchetypeMatch
1825
1826 // Traversed-source snapshot caching is only effective for traversed source terms.
1828 data.cacheSrcTrav = 0;
1829
1831
1832 // Calculate the component mask for simple queries
1833 isComplex |= ((data.as_mask_0 + data.as_mask_1) != 0);
1834 if (isComplex) {
1835 data.queryMask = {};
1837 } else {
1838 data.queryMask = build_entity_mask(EntitySpan{idsNoSrc.data(), idsNoSrcCnt});
1840 }
1841 }
1842
1843 // Request recompilation of the query if the mask has changed
1845
1846 // Request recompilation of the query if the mask has changed
1847 if (mask0_old != data.as_mask_0 || mask1_old != data.as_mask_1 ||
1848 isComplex_old != (data.flags & QueryFlags::Complex) ||
1849 hasSourceTerms_old != (data.flags & QueryFlags::HasSourceTerms) ||
1850 hasVariableTerms_old != (data.flags & QueryFlags::HasVariableTerms) ||
1851 canDirectTargetEval_old != data.canDirectTargetEval ||
1852 canDirectEntitySeedEvalShape_old != data.canDirectEntitySeedEvalShape ||
1853 hasOnlyDirectOrTerms_old != data.hasOnlyDirectOrTerms ||
1854 canReuseDynamicCache_old != data.canReuseDynamicCache || cachePolicy_old != data.cachePolicy ||
1855 createArchetypeMatchKind_old != data.createArchetypeMatchKind ||
1856 dynamicCacheKind_old != data.dynamicCacheKind || dependencyFlags_old != data.deps.flags ||
1857 createSelectorCnt_old != data.deps.createSelectorCnt || exclusionCnt_old != data.deps.exclusionCnt ||
1858 relationCnt_old != data.deps.relationCnt || sourceEntityCnt_old != data.deps.sourceEntityCnt ||
1859 sourceTermCnt_old != data.deps.sourceTermCnt || createSelectors_old != data.deps.createSelectors ||
1860 exclusions_old != data.deps.exclusions || relations_old != data.deps.relations ||
1861 sourceEntities_old != data.deps.sourceEntities)
1863 }
1864
1869 GAIA_NODISCARD static bool
1870 equals_no_handle_assumption(const QueryCtx& leftCtx, const QueryCtx& rightCtx) noexcept {
1871 // Lookup hash must match
1872 if (leftCtx.hashLookup != rightCtx.hashLookup)
1873 return false;
1874
1875 const auto& left = leftCtx.data;
1876 const auto& right = rightCtx.data;
1877 return left.identity_payload_equal(right);
1878 }
1879
1883 GAIA_NODISCARD bool operator==(const QueryCtx& other) const noexcept {
1884 // Comparison expected to be done only the first time the query is set up
1885 GAIA_ASSERT(q.handle.id() == QueryIdBad);
1886 // Fast path when cache ids are set
1887 // if (queryId != QueryIdBad && queryId == other.queryId)
1888 // return true;
1889
1890 return equals_no_handle_assumption(*this, other);
1891 }
1892
1896 GAIA_NODISCARD bool operator!=(const QueryCtx& other) const noexcept {
1897 return !operator==(other);
1898 }
1899 };
1900
1907 constexpr bool operator()(const QueryTerm& lhs, const QueryTerm& rhs) const {
1908 return query_term_less_for_lookup(lhs, rhs);
1909 }
1910 };
1911
1914 inline void sort(QueryCtx& ctx) {
1915 const uint32_t idsCnt = ctx.data.idsCnt;
1916 const uint32_t changedCnt = ctx.data.changedCnt;
1917
1918 auto& ctxData = ctx.data;
1919 // Canonicalize degenerate OR queries: a single OR term has AND semantics.
1920 // Rewriting it here keeps ordering/hash behavior identical to an explicit ALL term.
1921 if (idsCnt > 0) {
1922 uint32_t orCnt = 0;
1923 uint32_t orIdx = BadIndex;
1924 GAIA_FOR(idsCnt) {
1925 if (ctxData.terms[i].op != QueryOpKind::Or)
1926 continue;
1927 ++orCnt;
1928 orIdx = i;
1929 if (orCnt > 1)
1930 break;
1931 }
1932
1933 if (orCnt == 1)
1934 ctxData.terms[orIdx].op = QueryOpKind::All;
1935 }
1936
1937 // Sort data. Necessary for correct hash calculation.
1938 // Without sorting query.all<XXX, YYY> would be different than query.all<YYY, XXX>.
1939 // Also makes sure data is in optimal order for query processing.
1940 core::sort(
1941 ctxData.terms.data(), ctxData.terms.data() + ctxData.idsCnt, query_sort_cond{}, //
1942 [&](uint32_t left, uint32_t right) {
1943 core::swap(ctxData.ids[left], ctxData.ids[right]);
1944 core::swap(ctxData.terms[left], ctxData.terms[right]);
1945
1946 // Make sure masks remains correct after sorting
1947 core::swap_bits(ctxData.readWriteMask, left, right);
1948 core::swap_bits(ctxData.as_mask_0, left, right);
1949 core::swap_bits(ctxData.as_mask_1, left, right);
1950 });
1951
1952 if (idsCnt > 0) {
1953 uint32_t i = 0;
1954 while (i < idsCnt && ctxData.terms[i].op == QueryOpKind::All)
1955 ++i;
1956 ctxData.firstOr = (uint8_t)i;
1957 while (i < idsCnt && ctxData.terms[i].op == QueryOpKind::Or)
1958 ++i;
1959 ctxData.firstNot = (uint8_t)i;
1960 while (i < idsCnt && ctxData.terms[i].op == QueryOpKind::Not)
1961 ++i;
1962 ctxData.firstAny = (uint8_t)i;
1963 } else
1964 ctxData.firstOr = ctxData.firstNot = ctxData.firstAny = 0;
1965
1966 // Canonicalize filter order. This enables monotonic component lookup in filter matching
1967 // and keeps cache keys stable regardless of changed() call order.
1968 if (changedCnt > 1) {
1969 core::sort(ctxData.changed.data(), ctxData.changed.data() + changedCnt, SortComponentCond{});
1970 }
1971
1972 GAIA_FOR(changedCnt) {
1973 const auto comp = ctxData.changed[i];
1974 uint32_t compIdx = 0;
1975 while (compIdx < idsCnt && ctxData.ids[compIdx] != comp)
1976 ++compIdx;
1977
1978 GAIA_ASSERT(compIdx < idsCnt);
1979 ctxData.changedFields[i] = compIdx < idsCnt ? (uint8_t)compIdx : (uint8_t)0xFF;
1980 }
1981 }
1982
1986 inline void normalize_cache_src_trav(QueryCtx& ctx) {
1987 auto& ctxData = ctx.data;
1988 if (ctxData.cacheSrcTrav == 0)
1989 return;
1990
1991 bool hasTraversedSourceTerm = false;
1992 for (const auto& term: ctxData.terms_view()) {
1993 if (term.src == EntityBad || term.entTrav == EntityBad)
1994 continue;
1995
1996 hasTraversedSourceTerm = true;
1997 break;
1998 }
1999
2000 if (!hasTraversedSourceTerm)
2001 ctxData.cacheSrcTrav = 0;
2002 }
2003
2006 inline void calc_lookup_hash(QueryCtx& ctx) {
2007 GAIA_ASSERT(ctx.cc != nullptr);
2008 // Make sure we don't calculate the hash twice
2009 GAIA_ASSERT(ctx.hashLookup.hash == 0);
2010
2011 ctx.hashLookup = ctx.data.calc_lookup_hash();
2012 }
2013
2021 template <uint32_t MAX_COMPONENTS>
2022 GAIA_NODISCARD inline uint32_t comp_idx(const QueryTerm* pTerms, Entity entity, Entity src) {
2023 // We let the compiler know the upper iteration bound at compile-time.
2024 // This way it can optimize better (e.g. loop unrolling, vectorization).
2025 GAIA_FOR(MAX_COMPONENTS) {
2026 if (pTerms[i].id == entity && pTerms[i].src == src)
2027 return i;
2028 }
2029
2030 GAIA_ASSERT(false);
2031 return BadIndex;
2032 }
2033 } // namespace ecs
2034} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
void clear() noexcept
Removes all elements.
Definition darray_impl.h:449
GAIA_NODISCARD bool empty() const noexcept
Checks whether the container has no elements.
Definition darray_impl.h:510
void pop_back() noexcept
Removes the last element.
Definition darray_impl.h:342
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:193
void push_back(const T &arg)
Appends an element.
Definition darray_impl.h:309
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:193
void push_back(ComponentIndexEntry entry)
Appends a reverse-index record, spilling inline storage when necessary.
Definition query_common.h:300
GAIA_NODISCARD size_type size() const noexcept
Returns the number of reverse-index records.
Definition query_common.h:228
GAIA_NODISCARD ComponentIndexEntry & back() noexcept
Returns the final mutable record.
Definition query_common.h:286
GAIA_NODISCARD bool empty() const noexcept
Checks whether the bucket contains no reverse-index records.
Definition query_common.h:222
GAIA_NODISCARD const ComponentIndexEntry & operator[](size_type idx) const noexcept
Returns a read-only record by index.
Definition query_common.h:279
GAIA_NODISCARD const ComponentIndexEntry * data() const noexcept
Returns read-only contiguous record storage.
Definition query_common.h:240
GAIA_NODISCARD ComponentIndexEntry * end() noexcept
Returns a mutable iterator past the final record.
Definition query_common.h:258
GAIA_NODISCARD ComponentIndexEntry * begin() noexcept
Returns a mutable iterator to the first record.
Definition query_common.h:246
GAIA_NODISCARD ComponentIndexEntry * data() noexcept
Returns mutable contiguous record storage.
Definition query_common.h:234
GAIA_NODISCARD const ComponentIndexEntry * begin() const noexcept
Returns a read-only iterator to the first record.
Definition query_common.h:252
uint32_t size_type
Unsigned element-count and index type.
Definition query_common.h:198
GAIA_NODISCARD const ComponentIndexEntry * end() const noexcept
Returns a read-only iterator past the final record.
Definition query_common.h:264
GAIA_NODISCARD const ComponentIndexEntry & back() const noexcept
Returns the final read-only record.
Definition query_common.h:293
GAIA_NODISCARD ComponentIndexEntry & operator[](size_type idx) noexcept
Returns a mutable record by index.
Definition query_common.h:271
void pop_back()
Removes the final record and restores inline storage when one record remains.
Definition query_common.h:319
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:262
Same API as ser_buffer_binary, but backed by fully dynamic storage.
Definition ser_buffer_binary.h:161
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:175
GAIA_NODISCARD bool matches(const Archetype *pOther) const
Tests whether this record belongs to an archetype.
Definition query_common.h:186
uint16_t matchCount
Number of archetype components contributing this lookup-key match.
Definition query_common.h:181
Archetype * pArchetype
Archetype matched by the indexed entity or pair key.
Definition query_common.h:177
uint16_t compIdx
Component column index in pArchetype, or ComponentIndexBad when no direct column exists.
Definition query_common.h:179
Hashmap lookup structure used for Entity.
Definition id.h:543
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:296
GAIA_NODISCARD constexpr auto value() const noexcept
Raw identifier value.
Definition id.h:395
GAIA_NODISCARD constexpr auto id() const noexcept
Entity index in the entity array.
Definition id.h:359
Explicit component/entity access declarations used for scheduling decisions.
Definition query_common.h:675
GAIA_NODISCARD QueryAccess access(Entity entity) const
Returns explicitly declared access for an id.
Definition query_common.h:722
uint8_t readCnt
Number of valid entries in reads.
Definition query_common.h:681
GAIA_NODISCARD std::span< const Entity > writes_view() const
Returns the explicitly declared write ids.
Definition query_common.h:693
void add_read(Entity entity)
Declares that an id is read.
Definition query_common.h:699
cnt::sarray< Entity, MAX_ITEMS_IN_QUERY > reads
Component/entity ids read by the callback outside the query terms.
Definition query_common.h:677
GAIA_NODISCARD std::span< const Entity > reads_view() const
Returns the explicitly declared read ids.
Definition query_common.h:687
uint8_t writeCnt
Number of valid entries in writes.
Definition query_common.h:683
cnt::sarray< Entity, MAX_ITEMS_IN_QUERY > writes
Component/entity ids written by the callback outside the query terms.
Definition query_common.h:679
void add_write(Entity entity)
Declares that an id is written.
Definition query_common.h:710
Incremental query-matching cursor for one entity-to-archetype lookup bucket.
Definition query_common.h:159
uint32_t index
Number of bucket records that were already matched at revision.
Definition query_common.h:161
uint32_t revision
Lookup-bucket revision associated with index.
Definition query_common.h:163
Deduplicated entities and flags that can invalidate or update a query cache.
Definition query_common.h:1000
void set_dep_flag(DependencyFlags dependency)
Records a dependency fact.
Definition query_common.h:1058
GAIA_NODISCARD std::span< const Entity > exclusions_view() const
Returns negative selector ids used during archetype creation.
Definition query_common.h:1040
GAIA_NODISCARD bool has_dep_flag(DependencyFlags dependency) const
Tests whether a dependency fact was recorded.
Definition query_common.h:1065
DependencyFlags flags
Combined DependencyFlags describing the compiled query shape.
Definition query_common.h:1020
GAIA_NODISCARD std::span< const Entity > create_selectors_view() const
Returns positive selector ids used during archetype creation.
Definition query_common.h:1034
uint8_t sourceEntityCnt
Number of valid entries in sourceEntities.
Definition query_common.h:1016
GAIA_NODISCARD std::span< const Entity > src_entities_view() const
Returns concrete source-entity dependencies.
Definition query_common.h:1052
GAIA_NODISCARD std::span< const Entity > relations_view() const
Returns relation-version dependencies.
Definition query_common.h:1046
QueryEntityArray relations
Relations whose topology versions affect cached results.
Definition query_common.h:1006
uint8_t sourceTermCnt
Number of fixed-source terms, including duplicate source entities.
Definition query_common.h:1018
void clear()
Resets dependency counts and flags while retaining fixed storage.
Definition query_common.h:1023
QueryEntityArray createSelectors
Positive selector ids used for archetype-create propagation.
Definition query_common.h:1002
uint8_t createSelectorCnt
Number of valid entries in createSelectors.
Definition query_common.h:1010
uint8_t relationCnt
Number of valid entries in relations.
Definition query_common.h:1014
QueryEntityArray sourceEntities
Concrete source entities whose archetype versions affect cached results.
Definition query_common.h:1008
void add_rel(Entity relation)
Adds a unique relation-version dependency.
Definition query_common.h:1071
uint8_t exclusionCnt
Number of valid entries in exclusions.
Definition query_common.h:1012
GAIA_NODISCARD bool can_reuse_src_cache() const
Checks whether each fixed-source term has a distinct tracked entity.
Definition query_common.h:1091
void add_src_entity(Entity entity)
Adds a unique concrete source-entity dependency.
Definition query_common.h:1081
QueryEntityArray exclusions
Negative selector ids used to reject new archetypes.
Definition query_common.h:1004
Cold canonical payload used exclusively by shared-query hashing and equality.
Definition query_common.h:1097
QueryEntityArray changed
Canonicalized changed-filter ids reused by hash/equality for shared query dedup.
Definition query_common.h:1101
cnt::sarray< QueryTerm, MAX_ITEMS_IN_QUERY > lookupTerms
Canonicalized lookup terms reused by hash/equality for shared query dedup.
Definition query_common.h:1099
QueryEntityArray groupDeps
Canonicalized group dependency ids reused by hash/equality for shared query dedup.
Definition query_common.h:1103
Compact compiled query payload used by matching, identity, and cache maintenance.
Definition query_common.h:998
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:1152
uint8_t changedCnt
Number of valid changed-filter ids and field mappings.
Definition query_common.h:1119
QueryEntityArray groupDeps
Explicit grouping invalidation dependencies for custom group_by callbacks.
Definition query_common.h:1125
Entity sortBy
Entity to sort the archetypes by. EntityBad for no sorting.
Definition query_common.h:1127
TGroupByFunc groupByFunc
Function to use to perform the grouping.
Definition query_common.h:1133
uint8_t idsCnt
Number of valid ids and terms in the fixed-capacity query arrays.
Definition query_common.h:1117
GAIA_NODISCARD DynamicCacheKind calc_dynamic_cache_kind() const
Returns the dynamic-cache dependency shape derived from cache policy and dependencies.
Definition query_common.h:1276
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:1138
Entity groupBy
Entity to group the archetypes by. EntityBad for no grouping.
Definition query_common.h:1131
bool hasOnlyDirectOrTerms
True when the query contains only direct OR/NOT terms and at least one OR term.
Definition query_common.h:1166
void add_group_dep(Entity relation)
Adds a declared grouping invalidation dependency.
Definition query_common.h:1242
cnt::sarray< uint8_t, MAX_ITEMS_IN_QUERY > changedFields
Query term index for each changed-filter component after query canonicalization.
Definition query_common.h:1123
QueryEntityArray ids
Array of queried ids.
Definition query_common.h:1107
GAIA_NODISCARD QueryLookupHash::Type hash_lookup_key_payload() const
Returns the hash contribution from canonical lookup-key payload arrays.
Definition query_common.h:1385
GAIA_NODISCARD bool uses_src_trav_snapshot() const
Returns whether reusable dynamic-cache checks use a traversed source closure snapshot.
Definition query_common.h:1307
GAIA_NODISCARD bool has_sort_payload() const
Returns true when sort identity payload is active.
Definition query_common.h:1456
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:1141
bool canDirectEntitySeedEvalShape
True when the query shape is eligible for direct entity seed evaluation.
Definition query_common.h:1164
uint8_t firstOr
First OR record in pairs/ids/ops.
Definition query_common.h:1147
uint16_t cacheSrcTrav
Maximum allowed size of an explicitly cached traversed-source lookup closure.
Definition query_common.h:1156
GAIA_NODISCARD QueryLookupHash::Type hash_sort_payload() const
Returns the hash contribution from sort identity payload.
Definition query_common.h:1462
QueryMask queryMask
Component mask used for faster matching of simple queries.
Definition query_common.h:1135
uint8_t firstAny
First ANY record in pairs/ids/ops.
Definition query_common.h:1145
GAIA_NODISCARD std::span< QueryTerm > lookup_terms_view_mut()
Returns mutable canonicalized lookup terms used by shared query deduplication.
Definition query_common.h:1212
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:1472
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:1218
void refresh_lookup_keys()
Refreshes canonical lookup arrays used by shared query deduplication.
Definition query_common.h:1340
GAIA_NODISCARD std::span< const QueryTerm > lookup_terms_view() const
Returns canonicalized lookup terms used by shared query deduplication.
Definition query_common.h:1206
GAIA_NODISCARD std::span< const Entity > ids_view() const
Returns authored query ids in canonical execution order.
Definition query_common.h:1182
GAIA_NODISCARD std::span< const Entity > group_deps_view() const
Returns explicit grouping invalidation dependencies.
Definition query_common.h:1200
QueryArchetypeCacheIndexMap lastMatchedArchetypeIdx_All
Index of the last checked archetype in the component-to-archetype map.
Definition query_common.h:1111
CreateArchetypeMatchKind createArchetypeMatchKind
Create-time archetype matcher derived from query shape.
Definition query_common.h:1174
Entity directTargetEvalId
Term id used by the specialized direct-target evaluation shape.
Definition query_common.h:1160
void add_group_deps()
Adds all grouping invalidation relations to the dependency set.
Definition query_common.h:1252
GAIA_NODISCARD std::span< const uint8_t > changed_fields_view() const
Returns query-term indices matching changed-filter components.
Definition query_common.h:1194
QueryArchetypeCacheIndexMap lastMatchedArchetypeIdx_Or
Incremental lookup cursors for OR selector terms.
Definition query_common.h:1113
GAIA_NODISCARD QueryLookupHash calc_lookup_hash() const
Returns the finalized lookup hash for shared query identity.
Definition query_common.h:1511
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:1450
GAIA_NODISCARD std::span< const QueryTerm > terms_view() const
Returns compiled terms in execution order.
Definition query_common.h:1270
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:1301
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:1230
DynamicCacheKind dynamicCacheKind
Dynamic-cache dependency shape derived from compiled query metadata.
Definition query_common.h:1176
Dependencies deps
Explicit dependency metadata derived from query shape.
Definition query_common.h:1170
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:1236
TSortByFunc sortByFunc
Function to use to perform sorting.
Definition query_common.h:1129
uint16_t flags
Query flags.
Definition query_common.h:1154
bool canDirectTargetEval
True when the query can evaluate concrete target entities directly.
Definition query_common.h:1162
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:1224
GAIA_NODISCARD std::span< QueryTerm > terms_view_mut()
Returns mutable compiled terms in execution order.
Definition query_common.h:1265
uint8_t firstNot
First NOT record in pairs/ids/ops.
Definition query_common.h:1143
CachePolicy cachePolicy
Cache maintenance policy derived from query shape.
Definition query_common.h:1172
GAIA_NODISCARD bool calc_can_reuse_dynamic_cache() const
Returns whether the current query shape can reuse dynamic-cache results.
Definition query_common.h:1313
uint8_t groupDepCnt
Number of defined group dependencies.
Definition query_common.h:1149
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:1439
QueryEntityArray changed
Array of filtered components.
Definition query_common.h:1121
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:1352
GAIA_NODISCARD std::span< const Entity > changed_view() const
Returns changed-filter component ids.
Definition query_common.h:1188
GAIA_NODISCARD QueryLookupHash::Type hash_identity_payload() const
Returns the hash contribution from the full shared query identity payload.
Definition query_common.h:1502
cnt::sarray< QueryTerm, MAX_ITEMS_IN_QUERY > terms
Array of terms.
Definition query_common.h:1109
GAIA_NODISCARD QueryLookupHash::Type hash_grouping_payload() const
Returns the hash contribution from grouping identity payload.
Definition query_common.h:1492
LookupIdentity lookupIdentity
Cold canonical shared-query identity payload.
Definition query_common.h:1178
DirectTargetEvalKind directTargetEvalKind
Specialized direct-target evaluation shape for single-term queries.
Definition query_common.h:1158
QueryArchetypeCacheIndexMap lastMatchedArchetypeIdx_Not
Incremental lookup cursors for NOT selector terms.
Definition query_common.h:1115
bool canReuseDynamicCache
True when a dynamic cache can be reused by checking tracked runtime inputs.
Definition query_common.h:1168
Authored and compiled state defining query identity and execution behavior.
Definition query_common.h:885
GAIA_NODISCARD bool operator!=(const QueryCtx &other) const noexcept
Compares query contexts for distinct shared query identity.
Definition query_common.h:1896
struct gaia::ecs::QueryCtx::Data data
Compiled query payload.
ComponentCache * cc
Component cache.
Definition query_common.h:889
const World * w
World against which the query is compiled and executed.
Definition query_common.h:887
void refresh()
Rebuilds derived masks, dependency metadata, and cache policy after authored terms change.
Definition query_common.h:1526
GAIA_NODISCARD bool operator==(const QueryCtx &other) const noexcept
Compares query contexts during initial shared-query lookup.
Definition query_common.h:1883
QueryFlags
Query maintenance and execution flags derived during compilation.
Definition query_common.h:896
@ HasPrefabTerms
Query explicitly mentions Prefab and therefore bypasses automatic exclusion.
Definition query_common.h:914
@ SortGroups
Cached group ranges require sorting.
Definition query_common.h:902
@ MatchPrefab
Includes prefab entities without requiring an explicit Prefab term.
Definition query_common.h:912
@ Empty
No query flags are set.
Definition query_common.h:898
@ SortEntities
Cached entity slices require sorting.
Definition query_common.h:900
@ OrderGroups
Grouped archetypes are ordered by group identifier during cache refresh.
Definition query_common.h:916
@ Complex
Query requires the general matching path.
Definition query_common.h:904
@ HasVariableTerms
Query contains variable-based lookup terms.
Definition query_common.h:910
@ Recompile
VM opcode recompilation is pending.
Definition query_common.h:906
@ HasSourceTerms
Query contains fixed-source lookup terms.
Definition query_common.h:908
DependencyFlags
Dependency facts derived from the compiled term set.
Definition query_common.h:968
@ DependencyHasWildcardTerms
At least one wildcard id or pair term is present.
Definition query_common.h:982
@ DependencyHasPositiveTerms
At least one positive ALL or OR term is present.
Definition query_common.h:976
@ DependencyHasSourceTerms
At least one term uses a fixed source entity.
Definition query_common.h:972
@ DependencyHasEntityFilterTerms
At least one term requires per-entity filtering.
Definition query_common.h:990
@ DependencyHasVariableTerms
At least one term contains a runtime variable.
Definition query_common.h:974
@ DependencyHasPotentialInheritedIdTerms
A term shape may resolve through inherited-id matching.
Definition query_common.h:994
@ DependencyHasAnyTerms
At least one optional ANY term is present.
Definition query_common.h:980
@ DependencyHasInheritedDataTerms
Iteration requires cached inherited component pointers.
Definition query_common.h:992
@ DependencyHasTraversalTerms
At least one source term traverses a relation.
Definition query_common.h:988
@ DependencyHasSort
Query has an entity sorting callback.
Definition query_common.h:984
@ DependencyNone
No dependency facts are present.
Definition query_common.h:970
@ DependencyHasNegativeTerms
At least one negative NOT term is present.
Definition query_common.h:978
@ DependencyHasGroup
Query has an archetype grouping callback.
Definition query_common.h:986
QueryIdentity q
Query identity.
Definition query_common.h:893
CreateArchetypeMatchKind
Matcher selected for newly created archetypes.
Definition query_common.h:930
@ 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:954
@ Generic
Uses the general compiled query evaluator.
@ SingleAllSemanticIs
Evaluates one required semantic Is term.
@ SingleAllInherited
Evaluates one required term through inherited component data.
@ 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:1870
QueryLookupHash hashLookup
Lookup hash for this query.
Definition query_common.h:891
DynamicCacheKind
Dynamic-cache dependency shape derived from compiled query metadata.
Definition query_common.h:938
@ 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:920
@ 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:1520
Hashmap lookup structure used for Entity.
Definition query_common.h:422
QueryHandle handle() const
Returns the represented query handle.
Definition query_common.h:453
bool operator!=(const QueryHandleLookupKey &other) const
Compares lookup keys for inequality.
Definition query_common.h:476
bool operator==(const QueryHandleLookupKey &other) const
Compares lookup keys using their cached hashes and handles.
Definition query_common.h:466
static constexpr bool IsDirectHashKey
Indicates that Gaia containers may consume hash() without rehashing the key.
Definition query_common.h:438
QueryHandleLookupKey(QueryHandle handle)
Constructs a lookup key and precomputes its hash.
Definition query_common.h:443
size_t hash() const
Returns the precomputed container hash.
Definition query_common.h:459
Stable query-slot identifier combining a slot index and generation.
Definition query_common.h:356
static constexpr uint32_t IdMask
Bit mask spanning the query-id portion of a packed handle.
Definition query_common.h:358
GAIA_NODISCARD auto gen() const
Returns the query slot generation.
Definition query_common.h:408
GAIA_NODISCARD auto id() const
Returns the query slot identifier.
Definition query_common.h:403
GAIA_NODISCARD auto value() const
Returns the packed handle representation.
Definition query_common.h:413
GAIA_NODISCARD constexpr bool operator!=(const QueryHandle &other) const noexcept
Compares packed query handles for inequality.
Definition query_common.h:397
QueryHandle(QueryId id, uint32_t gen)
Constructs a handle from query slot metadata.
Definition query_common.h:377
Slot and serialization identity owned by a compiled query context.
Definition query_common.h:865
GAIA_NODISCARD QuerySerBuffer & ser_buffer(World *world)
Returns this query's serialization buffer in a world.
Definition query_common.h:874
QueryHandle handle
Query id.
Definition query_common.h:867
QueryId serId
Serialization id.
Definition query_common.h:869
void ser_buffer_reset(World *world)
Resets this query's serialization buffer in a world.
Definition query_common.h:879
User-provided query input.
Definition query_common.h:485
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:498
QueryTravKind travKind
Source traversal filter. Self means checking the source itself, Up means checking traversed ancestors...
Definition query_common.h:505
Entity id
Entity/Component/Pair to query.
Definition query_common.h:494
QueryAccess access
Access type.
Definition query_common.h:492
Entity entTrav
Optional traversal relation for source lookups. When set, the lookup starts at src and then walks rel...
Definition query_common.h:501
static constexpr uint8_t TravDepthUnlimited
Traversal-depth value selecting the internally bounded unlimited mode.
Definition query_common.h:487
QueryOpKind op
Operation to perform with the input.
Definition query_common.h:490
QueryMatchKind matchKind
Match semantics for terms with special meaning, such as Pair(Is, X).
Definition query_common.h:510
uint8_t travDepth
Maximum number of traversal steps. 0 means unlimited traversal depth (bounded internally,...
Definition query_common.h:508
Additional options for query terms. This can be used to configure source lookup, traversal and access...
Definition query_common.h:516
QueryTermOptions & trav_self_parent(Entity relation=ChildOf)
Checks the source and its immediate relation target.
Definition query_common.h:576
uint8_t travDepth
Maximum number of traversal steps. 0 means unlimited traversal depth (bounded internally,...
Definition query_common.h:528
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:634
Entity entSrc
Source entity to query from.
Definition query_common.h:521
QueryTermOptions & trav_self_down(Entity relation=ChildOf)
Checks the source and recursively traverses entities targeting it.
Definition query_common.h:596
QueryTermOptions & trav(Entity relation=ChildOf)
Traverses the source and its relation targets without a user depth limit.
Definition query_common.h:546
QueryTermOptions & trav_child(Entity relation=ChildOf)
Checks only immediate entities targeting the source.
Definition query_common.h:606
Entity entTrav
Optional traversal relation used for source lookup.
Definition query_common.h:523
QueryAccess access
Access mode for the term. When None, typed query terms infer read/write access from template mutabili...
Definition query_common.h:531
QueryMatchKind matchKind
Match semantics for terms with special meaning, such as Pair(Is, X).
Definition query_common.h:533
QueryTermOptions & src(Entity source)
Selects a fixed or variable source entity for the term.
Definition query_common.h:538
QueryTermOptions & in()
Allows direct and inherited matches for the term.
Definition query_common.h:662
QueryTravKind travKind
Source traversal filter.
Definition query_common.h:525
QueryTermOptions & trav_kind(QueryTravKind kind)
Replaces the traversal filter without changing its relation or depth.
Definition query_common.h:626
static constexpr uint8_t TravDepthUnlimited
Traversal-depth value selecting the internally bounded unlimited mode.
Definition query_common.h:518
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:566
QueryTermOptions & trav_down(Entity relation=ChildOf)
Traverses entities targeting the source without a user depth limit.
Definition query_common.h:586
QueryTermOptions & direct()
Restricts the term to direct storage matches.
Definition query_common.h:655
QueryTermOptions & trav_self_child(Entity relation=ChildOf)
Checks the source and immediate entities targeting it.
Definition query_common.h:616
QueryTermOptions & trav_up(Entity relation=ChildOf)
Traverses relation targets without checking the source itself.
Definition query_common.h:556
Internal representation of QueryInput.
Definition query_common.h:732
bool operator==(const QueryTerm &other) const
Compares the matching identity of two compiled terms.
Definition query_common.h:755
Entity id
Queried id.
Definition query_common.h:734
Archetype * srcArchetype
Archetype of the src entity.
Definition query_common.h:746
uint8_t travDepth
Maximum number of traversal steps.
Definition query_common.h:742
QueryMatchKind matchKind
Match semantics for this term.
Definition query_common.h:744
bool operator!=(const QueryTerm &other) const
Compares the matching identity of two compiled terms for inequality.
Definition query_common.h:762
uint8_t fieldIndex
Stable execution field index matching the user-defined query field order.
Definition query_common.h:750
Entity entTrav
Optional traversal relation for source lookups.
Definition query_common.h:738
QueryOpKind op
Operation to perform with the term.
Definition query_common.h:748
Entity src
Source of where the queried id is looked up at.
Definition query_common.h:736
QueryTravKind travKind
Source traversal filter.
Definition query_common.h:740
One lookup-key record collected while indexing a single archetype.
Definition query_common.h:333
EntityLookupKey key
Entity or wildcard-pair lookup key represented by this record.
Definition query_common.h:335
ComponentIndexEntry entry
Reverse-index data associated with key.
Definition query_common.h:337
GAIA_NODISCARD bool matches(EntityLookupKey other) const
Tests whether this item represents a lookup key.
Definition query_common.h:342
Functor for sorting terms in a query before compilation.
Definition query_common.h:1902
constexpr bool operator()(const QueryTerm &lhs, const QueryTerm &rhs) const
Orders two terms by their canonical lookup representation.
Definition query_common.h:1907