Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
query_info.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include "gaia/cnt/darray.h"
5#include "gaia/cnt/ilist.h"
6#include "gaia/cnt/set.h"
7#include "gaia/config/profiler.h"
8#include "gaia/core/hashing_policy.h"
9#include "gaia/core/utility.h"
10#include "gaia/ecs/api.h"
11#include "gaia/ecs/archetype.h"
12#include "gaia/ecs/archetype_common.h"
13#include "gaia/ecs/component.h"
14#include "gaia/ecs/component_cache.h"
15#include "gaia/ecs/id.h"
16#include "gaia/ecs/query_common.h"
17#include "gaia/ecs/vm.h"
18#include "gaia/mem/mem_utils.h"
19
20namespace gaia {
21 namespace ecs {
22 struct Entity;
23 class World;
24
25 using EntityToArchetypeMap = cnt::map<EntityLookupKey, ArchetypeDArray>;
27 GroupId groupId = 0;
28 uint8_t indices[ChunkHeader::MAX_COMPONENTS];
29 };
30
32 QueryCtx* pQueryCtx;
33 const EntityToArchetypeMap* pEntityToArchetypeMap;
34 const ArchetypeDArray* pAllArchetypes;
35 };
36
37 class QueryInfo: public cnt::ilist_item {
38 public:
40 enum class MatchArchetypeQueryRet : uint8_t { Fail, Ok, Skip };
41
42 private:
43 struct Instruction {
44 Entity id;
45 QueryOpKind op;
46 };
47
48 struct SortData {
49 Chunk* pChunk;
50 uint32_t archetypeIdx;
51 uint16_t startRow;
52 uint16_t count;
53 };
54
55 struct GroupData {
56 GroupId groupId;
57 uint32_t idxFirst;
58 uint32_t idxLast;
59 bool needsSorting;
60 };
61
62 uint32_t m_refs = 0;
63
65 QueryCtx m_ctx;
67 vm::VirtualMachine m_vm;
68
71 cnt::set<Archetype*> m_archetypeSet;
73 ArchetypeDArray m_archetypeCache;
75 cnt::darray<ArchetypeCacheData> m_archetypeCacheData;
76
78 cnt::darray<SortData> m_archetypeSortData;
80 cnt::darray<GroupData> m_archetypeGroupData;
81
83 ArchetypeId m_lastArchetypeId{};
85 uint32_t m_worldVersion{};
86
87 enum QueryCmdType : uint8_t { ALL, ANY, NOT };
88
89 template <typename TType>
90 GAIA_NODISCARD bool has_inter([[maybe_unused]] QueryOpKind op, bool isReadWrite) const {
91 using T = core::raw_t<TType>;
92
93 if constexpr (std::is_same_v<T, Entity>) {
94 // Entities are read-only.
95 GAIA_ASSERT(!isReadWrite);
96 // Skip Entity input args. Entities are always there.
97 return true;
98 } else {
99 Entity id;
100
101 if constexpr (is_pair<T>::value) {
102 const auto rel = m_ctx.cc->get<typename T::rel>().entity;
103 const auto tgt = m_ctx.cc->get<typename T::tgt>().entity;
104 id = (Entity)Pair(rel, tgt);
105 } else {
106 id = m_ctx.cc->get<T>().entity;
107 }
108
109 const auto& ctxData = m_ctx.data;
110 const auto compIdx = comp_idx<MAX_ITEMS_IN_QUERY>(ctxData._terms.data(), id, EntityBad);
111
112 if (op != ctxData._terms[compIdx].op)
113 return false;
114
115 // Read-write mask must match
116 const uint32_t maskRW = (uint32_t)ctxData.readWriteMask & (1U << compIdx);
117 const uint32_t maskXX = (uint32_t)isReadWrite << compIdx;
118 return maskRW == maskXX;
119 }
120 }
121
122 template <typename T>
123 GAIA_NODISCARD bool has_inter(QueryOpKind op) const {
124 // static_assert(is_raw_v<<T>, "has() must be used with raw types");
125 constexpr bool isReadWrite = core::is_mut_v<T>;
126 return has_inter<T>(op, isReadWrite);
127 }
128
129 public:
130 void add_ref() {
131 ++m_refs;
132 GAIA_ASSERT(m_refs != 0);
133 }
134
135 void dec_ref() {
136 GAIA_ASSERT(m_refs > 0);
137 --m_refs;
138 }
139
140 uint32_t refs() const {
141 return m_refs;
142 }
143
144 void init(World* world) {
145 m_ctx.w = world;
146 }
147
148 void reset() {
149 m_archetypeSet = {};
150 m_archetypeCache = {};
151 m_archetypeSortData = {};
152 m_archetypeCacheData = {};
153 m_archetypeGroupData = {};
154 m_lastArchetypeId = 0;
155
156 m_ctx.data.lastMatchedArchetypeIdx_All = {};
157 m_ctx.data.lastMatchedArchetypeIdx_Any = {};
158 m_ctx.data.lastMatchedArchetypeIdx_Not = {};
159 }
160
161 GAIA_NODISCARD static QueryInfo create(
162 QueryId id, QueryCtx&& ctx, const EntityToArchetypeMap& entityToArchetypeMap,
163 const ArchetypeDArray& allArchetypes) {
164 // Make sure query items are sorted
165 sort(ctx);
166
167 QueryInfo info;
168 info.idx = id;
169 info.data.gen = 0;
170
171 info.m_ctx = GAIA_MOV(ctx);
172 info.m_ctx.q.handle = {id, 0};
173
174 // Compile the query
175 info.compile(entityToArchetypeMap, allArchetypes);
176
177 return info;
178 }
179
180 GAIA_NODISCARD static QueryInfo create(uint32_t idx, uint32_t gen, void* pCtx) {
181 auto* pCreationCtx = (QueryInfoCreationCtx*)pCtx;
182 auto& queryCtx = *pCreationCtx->pQueryCtx;
183 auto& entityToArchetypeMap = (EntityToArchetypeMap&)*pCreationCtx->pEntityToArchetypeMap;
184 auto& allArchetypes = (ArchetypeDArray&)*pCreationCtx->pAllArchetypes;
185
186 // Make sure query items are sorted
187 sort(queryCtx);
188
189 QueryInfo info;
190 info.idx = idx;
191 info.data.gen = gen;
192
193 info.m_ctx = GAIA_MOV(queryCtx);
194 info.m_ctx.q.handle = {idx, gen};
195
196 // Compile the query
197 info.compile(entityToArchetypeMap, allArchetypes);
198
199 return info;
200 }
201
202 GAIA_NODISCARD static QueryHandle handle(const QueryInfo& info) {
203 return QueryHandle(info.idx, info.data.gen);
204 }
205
207 void compile(const EntityToArchetypeMap& entityToArchetypeMap, const ArchetypeDArray& allArchetypes) {
208 GAIA_PROF_SCOPE(queryinfo::compile);
209
210 // Compile the opcodes
211 m_vm.compile(entityToArchetypeMap, allArchetypes, m_ctx);
212 }
213
215 void recompile() {
216 GAIA_PROF_SCOPE(queryinfo::recompile);
217
218 // Compile the opcodes
219 m_vm.create_opcodes(m_ctx);
220 }
221
222 void set_world_version(uint32_t version) {
223 m_worldVersion = version;
224 }
225
226 GAIA_NODISCARD uint32_t world_version() const {
227 return m_worldVersion;
228 }
229
230 GAIA_NODISCARD bool operator==(const QueryCtx& other) const {
231 return m_ctx == other;
232 }
233
234 GAIA_NODISCARD bool operator!=(const QueryCtx& other) const {
235 return m_ctx != other;
236 }
237
244 void match(
245 // entity -> archetypes mapping
246 const EntityToArchetypeMap& entityToArchetypeMap,
247 // all archetypes in the world
248 const ArchetypeDArray& allArchetypes,
249 // last matched archetype id
250 ArchetypeId archetypeLastId) {
251
252 // Global temporary buffers for collecting archetypes that match a query.
253 static cnt::set<Archetype*> s_tmpArchetypeMatchesSet;
254 static ArchetypeDArray s_tmpArchetypeMatchesArr;
255
256 struct CleanUpTmpArchetypeMatches {
257 CleanUpTmpArchetypeMatches() = default;
258 CleanUpTmpArchetypeMatches(const CleanUpTmpArchetypeMatches&) = delete;
259 CleanUpTmpArchetypeMatches(CleanUpTmpArchetypeMatches&&) = delete;
260 CleanUpTmpArchetypeMatches& operator=(const CleanUpTmpArchetypeMatches&) = delete;
261 CleanUpTmpArchetypeMatches& operator=(CleanUpTmpArchetypeMatches&&) = delete;
262
263 ~CleanUpTmpArchetypeMatches() {
264 // When the scope ends, we clear the arrays.
265 // Note, no memory is released. Allocated capacity remains unchanged
266 // because we do not want to kill time with allocating memory all the time.
267 s_tmpArchetypeMatchesSet.clear();
268 s_tmpArchetypeMatchesArr.clear();
269 }
270 } autoCleanup;
271
272 auto& ctxData = m_ctx.data;
273
274 // Recompile if necessary
275 if ((ctxData.flags & QueryCtx::QueryFlags::Recompile) != 0)
276 recompile();
277
278 // Skip if nothing has been compiled.
279 if (!m_vm.is_compiled())
280 return;
281
282 // Skip if no new archetype appeared
283 GAIA_ASSERT(archetypeLastId >= m_lastArchetypeId);
284 if (m_lastArchetypeId == archetypeLastId) {
285 // Sort entities if necessary
286 sort_entities();
287 return;
288 }
289
290 m_lastArchetypeId = archetypeLastId;
291
292 GAIA_PROF_SCOPE(queryinfo::match);
293
294 // Prepare the context
295 vm::MatchingCtx ctx{};
296 ctx.pWorld = world();
297 ctx.pAllArchetypes = &allArchetypes;
298 ctx.pEntityToArchetypeMap = &entityToArchetypeMap;
299 ctx.pMatchesArr = &s_tmpArchetypeMatchesArr;
300 ctx.pMatchesSet = &s_tmpArchetypeMatchesSet;
301 ctx.pLastMatchedArchetypeIdx_All = &ctxData.lastMatchedArchetypeIdx_All;
302 ctx.pLastMatchedArchetypeIdx_Any = &ctxData.lastMatchedArchetypeIdx_Any;
303 ctx.pLastMatchedArchetypeIdx_Not = &ctxData.lastMatchedArchetypeIdx_Not;
304 ctx.queryMask = ctxData.queryMask;
305 ctx.as_mask_0 = ctxData.as_mask_0;
306 ctx.as_mask_1 = ctxData.as_mask_1;
307 ctx.flags = ctxData.flags;
308
309 // Run the virtual machine
310 m_vm.exec(ctx);
311
312 // Write found matches to cache
313 for (auto* pArchetype: *ctx.pMatchesArr)
314 add_archetype_to_cache(pArchetype);
315
316 // Sort entities if necessary
317 sort_entities();
318 // Sort cache groups if necessary
319 sort_cache_groups();
320 }
321
325 GAIA_PROF_SCOPE(queryinfo::calc_sort_data);
326
327 m_archetypeSortData.clear();
328
329 // The function doesn't do any moves and expects that all chunks have their data sorted already.
330 // We use a min-heap / priority queue - like structure during query iteration to merge sorted tables:
331 // - we hold a cursor into each sorted chunk
332 // - we compare the next entity from each chunk using your sorting function
333 // - we then pick the smallest one (like k-way merge sort), and advance that cursor
334 // This is esentially what this function does:
335 // while (any_chunk_has_entities) {
336 // find_chunk_with_smallest_next_element();
337 // yield(entity_from_that_chunk);
338 // advance_cursor_for_that_chunk();
339 // }
340 // This produces a globally sorted view without modifying actual data. It's a balance between
341 // performance and memory usage. We could also sort the data in-place across all chunks, but that
342 // would generated too many data moves (entities + all of their components).
343
344 struct Cursor {
345 uint32_t chunkIdx = 0;
346 uint16_t row = 0;
347 };
348
349 auto& archetypes = m_archetypeCache;
350
351 // Initialize cursors. We will need as many as there are archetypes.
352 cnt::sarray_ext<Cursor, 128> cursors(archetypes.size());
353
354 uint32_t currArchetypeIdx = (uint32_t)-1;
355 Chunk* pCurrentChunk = nullptr;
356 uint16_t currentStartRow = 0;
357 uint16_t currentRow = 0;
358
359 const void* pDataMin = nullptr;
360 const void* pDataCurr = nullptr;
361
362 while (true) {
363 uint32_t minArchetypeIdx = (uint32_t)-1;
364 Entity minEntity = EntityBad;
365
366 // Find the next entity across all tables/chunks
367 for (uint32_t t = 0; t < archetypes.size(); ++t) {
368 const auto* pArchetype = archetypes[t];
369 const auto& chunks = pArchetype->chunks();
370 auto& cur = cursors[t];
371
372 while (cur.chunkIdx < chunks.size() && cur.row >= chunks[cur.chunkIdx]->size()) {
373 ++cur.chunkIdx;
374 cur.row = 0;
375 }
376
377 if (cur.chunkIdx >= chunks.size())
378 continue;
379
380 const auto* pChunk = pArchetype->chunks()[cur.chunkIdx];
381 auto entity = pChunk->entity_view()[cur.row];
382
383 if (m_ctx.data.sortBy != ecs::EntityBad) {
384 const auto compIdx = pChunk->comp_idx(m_ctx.data.sortBy);
385 pDataCurr = pChunk->comp_ptr(compIdx, cur.row);
386 } else
387 pDataCurr = &pChunk->entity_view()[cur.row];
388
389 if (minEntity == EntityBad) {
390 minEntity = entity;
391 minArchetypeIdx = t;
392 pDataMin = pDataCurr;
393 continue;
394 }
395
396 if (m_ctx.data.sortByFunc(*m_ctx.w, pDataCurr, pDataMin) < 0) {
397 minEntity = entity;
398 minArchetypeIdx = t;
399 }
400 }
401
402 // No more results found, we can stop
403 if (minArchetypeIdx == (uint32_t)-1)
404 break;
405
406 auto& cur = cursors[minArchetypeIdx];
407 const auto& chunks = archetypes[minArchetypeIdx]->chunks();
408 Chunk* pChunk = chunks[cur.chunkIdx];
409
410 if (minArchetypeIdx == currArchetypeIdx && pChunk == pCurrentChunk) {
411 // Current slice
412 } else {
413 // End previous slice
414 if (pCurrentChunk != nullptr) {
415 m_archetypeSortData.push_back(
416 {pCurrentChunk, currArchetypeIdx, currentStartRow, (uint16_t)(currentRow - currentStartRow)});
417 }
418
419 // Start a new slice
420 currArchetypeIdx = minArchetypeIdx;
421 pCurrentChunk = pChunk;
422 currentStartRow = cur.row;
423 }
424
425 ++cur.row;
426 currentRow = cur.row;
427 }
428
429 if (pCurrentChunk != nullptr) {
430 m_archetypeSortData.push_back(
431 {pCurrentChunk, currArchetypeIdx, currentStartRow, (uint16_t)(currentRow - currentStartRow)});
432 }
433 }
434
435 void sort_entities() {
436 if (m_ctx.data.sortByFunc == nullptr)
437 return;
438
439 if ((m_ctx.data.flags & QueryCtx::QueryFlags::SortEntities) == 0) {
440 // TODO: We need observers to implement that would listen to entity movements within chunks.
441 // thanks to that we would know right away if some movement happenend without
442 // having to check this constantly.
443 bool hasChanged = false;
444 for (const auto* pArchetype: m_archetypeCache) {
445 const auto& chunks = pArchetype->chunks();
446 for (const auto* pChunk: chunks) {
447 if (pChunk->changed(m_worldVersion)) {
448 hasChanged = true;
449 break;
450 }
451 }
452 }
453 if (!hasChanged)
454 return;
455 }
456 m_ctx.data.flags ^= QueryCtx::QueryFlags::SortEntities;
457
458 // First, sort entities in archetypes
459 for (auto* pArchetype: m_archetypeCache)
460 pArchetype->sort_entities(m_ctx.data.sortBy, m_ctx.data.sortByFunc);
461
462 // Now that entites are sorted, we can start creating slices
464 }
465
466 void sort_cache_groups() {
467 if ((m_ctx.data.flags & QueryCtx::QueryFlags::SortGroups) == 0)
468 return;
469 m_ctx.data.flags ^= QueryCtx::QueryFlags::SortGroups;
470
471 struct sort_cond {
472 bool operator()(const ArchetypeCacheData& a, const ArchetypeCacheData& b) const {
473 return a.groupId <= b.groupId;
474 }
475 };
476
477 // Archetypes in cache are ordered by groupId. Adding a new archetype
478 // possibly means rearranging the existing ones.
479 // 2 2 3 3 3 3 4 4 4 [2]
480 // -->
481 // 2 2 [2] 3 3 3 3 4 4 4
482 core::sort(m_archetypeCacheData, sort_cond{}, [&](uint32_t left, uint32_t right) {
483 auto* pTmpArchetype = m_archetypeCache[left];
484 m_archetypeCache[left] = m_archetypeCache[right];
485 m_archetypeCache[right] = pTmpArchetype;
486
487 auto tmp = m_archetypeCacheData[left];
488 m_archetypeCacheData[left] = m_archetypeCacheData[right];
489 m_archetypeCacheData[right] = tmp;
490 });
491 }
492
493 ArchetypeCacheData create_cache_data(Archetype* pArchetype) {
494 ArchetypeCacheData cacheData;
495 auto queryIds = ctx().data.ids_view();
496 const auto cnt = (uint32_t)queryIds.size();
497 GAIA_FOR(cnt) {
498 const auto idxBeforeRemapping = m_ctx.data._remapping[i];
499 const auto queryId = queryIds[idxBeforeRemapping];
500 // compIdx can be -1. We are fine with it because the user should never ask for something
501 // that is not present on the archetype. If they do, they made a mistake.
502 const auto compIdx = core::get_index_unsafe(pArchetype->ids_view(), queryId);
503 GAIA_ASSERT(compIdx != BadIndex);
504
505 cacheData.indices[i] = (uint8_t)compIdx;
506 }
507 return cacheData;
508 }
509
510 void add_archetype_to_cache_no_grouping(Archetype* pArchetype) {
511 GAIA_PROF_SCOPE(queryinfo::add_cache_ng);
512
513 if (m_archetypeSet.contains(pArchetype))
514 return;
515
516 m_archetypeSet.emplace(pArchetype);
517 m_archetypeCache.push_back(pArchetype);
518 m_archetypeCacheData.push_back(create_cache_data(pArchetype));
519 }
520
521 void add_archetype_to_cache_w_grouping(Archetype* pArchetype) {
522 GAIA_PROF_SCOPE(queryinfo::add_cache_wg);
523
524 if (m_archetypeSet.contains(pArchetype))
525 return;
526
527 const GroupId groupId = m_ctx.data.groupByFunc(*m_ctx.w, *pArchetype, m_ctx.data.groupBy);
528
529 ArchetypeCacheData cacheData = create_cache_data(pArchetype);
530 cacheData.groupId = groupId;
531
532 if (m_archetypeGroupData.empty()) {
533 m_archetypeGroupData.push_back({groupId, 0, 0, false});
534 } else {
535 const auto cnt = m_archetypeGroupData.size();
536 GAIA_FOR(cnt) {
537 if (groupId < m_archetypeGroupData[i].groupId) {
538 // Insert the new group before one with a lower groupId.
539 // 2 3 5 10 20 25 [7]<-new group
540 // -->
541 // 2 3 5 [7] 10 20 25
542 m_archetypeGroupData.insert(
543 m_archetypeGroupData.begin() + i,
544 {groupId, m_archetypeGroupData[i].idxFirst, m_archetypeGroupData[i].idxFirst, false});
545 const auto lastGrpIdx = m_archetypeGroupData.size();
546
547 // Update ranges
548 for (uint32_t j = i + 1; j < lastGrpIdx; ++j) {
549 ++m_archetypeGroupData[j].idxFirst;
550 ++m_archetypeGroupData[j].idxLast;
551 }
552
553 // Resort groups
554 m_ctx.data.flags |= QueryCtx::QueryFlags::SortGroups;
555 goto groupWasFound;
556 } else if (m_archetypeGroupData[i].groupId == groupId) {
557 const auto lastGrpIdx = m_archetypeGroupData.size();
558 ++m_archetypeGroupData[i].idxLast;
559
560 // Update ranges
561 for (uint32_t j = i + 1; j < lastGrpIdx; ++j) {
562 ++m_archetypeGroupData[j].idxFirst;
563 ++m_archetypeGroupData[j].idxLast;
564 m_ctx.data.flags |= QueryCtx::QueryFlags::SortGroups;
565 }
566
567 goto groupWasFound;
568 }
569 }
570
571 {
572 // We have a new group
573 const auto groupsCnt = m_archetypeGroupData.size();
574 if (groupsCnt == 0) {
575 // No groups exist yet, the range is {0 .. 0}
576 m_archetypeGroupData.push_back( //
577 {groupId, 0, 0, false});
578 } else {
579 const auto& groupPrev = m_archetypeGroupData[groupsCnt - 1];
580 GAIA_ASSERT(groupPrev.idxLast + 1 == m_archetypeCache.size());
581 // The new group starts where the old one ends
582 m_archetypeGroupData.push_back(
583 {groupId, //
584 groupPrev.idxLast + 1, //
585 groupPrev.idxLast + 1, //
586 false});
587 }
588 }
589
590 groupWasFound:;
591 }
592
593 m_archetypeSet.emplace(pArchetype);
594 m_archetypeCache.push_back(pArchetype);
595 m_archetypeCacheData.push_back(GAIA_MOV(cacheData));
596 }
597
598 void add_archetype_to_cache(Archetype* pArchetype) {
599 if (m_ctx.data.sortByFunc != nullptr)
600 m_ctx.data.flags |= QueryCtx::QueryFlags::SortEntities;
601
602 if (m_ctx.data.groupBy != EntityBad)
603 add_archetype_to_cache_w_grouping(pArchetype);
604 else
605 add_archetype_to_cache_no_grouping(pArchetype);
606 }
607
608 bool del_archetype_from_cache(Archetype* pArchetype) {
609 const auto it = m_archetypeSet.find(pArchetype);
610 if (it == m_archetypeSet.end())
611 return false;
612 m_archetypeSet.erase(it);
613
614 if (m_ctx.data.sortByFunc != nullptr)
615 m_ctx.data.flags |= QueryCtx::QueryFlags::SortEntities;
616
617 const auto archetypeIdx = core::get_index_unsafe(m_archetypeCache, pArchetype);
618 GAIA_ASSERT(archetypeIdx != BadIndex);
619
620 core::swap_erase(m_archetypeCache, archetypeIdx);
621 core::swap_erase(m_archetypeCacheData, archetypeIdx);
622
623 // Update the group data if possible
624 if (m_ctx.data.groupBy != EntityBad) {
625 const auto groupId = m_ctx.data.groupByFunc(*m_ctx.w, *pArchetype, m_ctx.data.groupBy);
626 const auto grpIdx = core::get_index_if_unsafe(m_archetypeGroupData, [&](const GroupData& group) {
627 return group.groupId == groupId;
628 });
629 GAIA_ASSERT(grpIdx != BadIndex);
630
631 auto& currGrp = m_archetypeGroupData[archetypeIdx];
632
633 // Update ranges
634 const auto lastGrpIdx = m_archetypeGroupData.size();
635 for (uint32_t j = grpIdx + 1; j < lastGrpIdx; ++j) {
636 --m_archetypeGroupData[j].idxFirst;
637 --m_archetypeGroupData[j].idxLast;
638 }
639
640 // Handle the current group. If it's about to be left empty, delete it.
641 if (currGrp.idxLast - currGrp.idxFirst > 0)
642 --currGrp.idxLast;
643 else
644 m_archetypeGroupData.erase(m_archetypeGroupData.begin() + grpIdx);
645 }
646
647 return true;
648 }
649
650 GAIA_NODISCARD World* world() {
651 GAIA_ASSERT(m_ctx.w != nullptr);
652 return const_cast<World*>(m_ctx.w);
653 }
654 GAIA_NODISCARD const World* world() const {
655 GAIA_ASSERT(m_ctx.w != nullptr);
656 return m_ctx.w;
657 }
658
659 GAIA_NODISCARD QuerySerBuffer& ser_buffer() {
660 return m_ctx.q.ser_buffer(world());
661 }
662 void ser_buffer_reset() {
663 m_ctx.q.ser_buffer_reset(world());
664 }
665
666 GAIA_NODISCARD QueryCtx& ctx() {
667 return m_ctx;
668 }
669 GAIA_NODISCARD const QueryCtx& ctx() const {
670 return m_ctx;
671 }
672
673 GAIA_NODISCARD bool has_filters() const {
674 return m_ctx.data.changedCnt > 0;
675 }
676
677 template <typename... T>
678 bool has_any() const {
679 return (has_inter<T>(QueryOpKind::Any) || ...);
680 }
681
682 template <typename... T>
683 bool has_all() const {
684 return (has_inter<T>(QueryOpKind::All) && ...);
685 }
686
687 template <typename... T>
688 bool has_no() const {
689 return (!has_inter<T>(QueryOpKind::Not) && ...);
690 }
691
694 void remove(Archetype* pArchetype) {
695 GAIA_PROF_SCOPE(queryinfo::remove);
696
697 if (!del_archetype_from_cache(pArchetype))
698 return;
699
700 // An archetype was removed from the world so the last matching archetype index needs to be
701 // lowered by one for every component context.
702 auto clearMatches = [](QueryArchetypeCacheIndexMap& matches) {
703 for (auto& pair: matches) {
704 auto& lastMatchedArchetypeIdx = pair.second;
705 if (lastMatchedArchetypeIdx > 0)
706 --lastMatchedArchetypeIdx;
707 }
708 };
709 clearMatches(m_ctx.data.lastMatchedArchetypeIdx_All);
710 clearMatches(m_ctx.data.lastMatchedArchetypeIdx_Any);
711 }
712
714 std::span<const uint8_t> indices_mapping_view(uint32_t archetypeIdx) const {
715 const auto& ctxData = m_archetypeCacheData[archetypeIdx];
716 return {(const uint8_t*)&ctxData.indices[0], ChunkHeader::MAX_COMPONENTS};
717 }
718
719 GAIA_NODISCARD ArchetypeDArray::iterator begin() {
720 return m_archetypeCache.begin();
721 }
722
723 GAIA_NODISCARD ArchetypeDArray::const_iterator begin() const {
724 return m_archetypeCache.begin();
725 }
726
727 GAIA_NODISCARD ArchetypeDArray::const_iterator cbegin() const {
728 return m_archetypeCache.begin();
729 }
730
731 GAIA_NODISCARD ArchetypeDArray::iterator end() {
732 return m_archetypeCache.end();
733 }
734
735 GAIA_NODISCARD ArchetypeDArray::const_iterator end() const {
736 return m_archetypeCache.end();
737 }
738
739 GAIA_NODISCARD ArchetypeDArray::const_iterator cend() const {
740 return m_archetypeCache.end();
741 }
742
743 GAIA_NODISCARD std::span<const Archetype*> cache_archetype_view() const {
744 return std::span{(const Archetype**)m_archetypeCache.data(), m_archetypeCache.size()};
745 }
746
747 GAIA_NODISCARD std::span<const ArchetypeCacheData> cache_data_view() const {
748 return std::span{m_archetypeCacheData.data(), m_archetypeCacheData.size()};
749 }
750
751 GAIA_NODISCARD std::span<const SortData> cache_sort_view() const {
752 return std::span{m_archetypeSortData.data(), m_archetypeSortData.size()};
753 }
754
755 GAIA_NODISCARD std::span<const GroupData> group_data_view() const {
756 return std::span{m_archetypeGroupData.data(), m_archetypeGroupData.size()};
757 }
758 };
759 } // namespace ecs
760} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
Array of elements of type.
Definition sarray_ext_impl.h:27
Definition span_impl.h:99
Definition archetype.h:82
Definition chunk.h:29
uint8_t & data(uint32_t offset)
Returns a mutable pointer to chunk data.
Definition chunk.h:1168
Definition query_info.h:37
void remove(Archetype *pArchetype)
Removes an archetype from cache.
Definition query_info.h:694
void calculate_sort_data()
Calculates the sort data for the archetypes in the cache. This allows us to iterate entites in the or...
Definition query_info.h:324
void recompile()
Recompile the query.
Definition query_info.h:215
MatchArchetypeQueryRet
Query matching result.
Definition query_info.h:40
void compile(const EntityToArchetypeMap &entityToArchetypeMap, const ArchetypeDArray &allArchetypes)
Compile the query terms into a form we can easily process.
Definition query_info.h:207
void match(const EntityToArchetypeMap &entityToArchetypeMap, const ArchetypeDArray &allArchetypes, ArchetypeId archetypeLastId)
Tries to match the query against archetypes in entityToArchetypeMap. This is necessary so we do not i...
Definition query_info.h:244
std::span< const uint8_t > indices_mapping_view(uint32_t archetypeIdx) const
Returns a view of indices mapping for component entities in a given archetype.
Definition query_info.h:714
Wrapper for two types forming a relationship pair. Depending on what types are used to form a pair it...
Definition id.h:202
Definition robin_hood.h:720
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition ilist.h:14
uint32_t idx
Allocated items: Index in the list. Deleted items: Index of the next deleted item in the list.
Definition ilist.h:22
ItemData data
Item data.
Definition ilist.h:24
Definition query_info.h:26
static constexpr uint32_t MAX_COMPONENTS
Maximum number of components on archetype.
Definition chunk_header.h:53
Definition id.h:225
Definition query_common.h:197
Definition query_info.h:31
Definition vm.h:27