50 uint32_t archetypeIdx;
67 vm::VirtualMachine m_vm;
71 cnt::set<Archetype*> m_archetypeSet;
73 ArchetypeDArray m_archetypeCache;
75 cnt::darray<ArchetypeCacheData> m_archetypeCacheData;
78 cnt::darray<SortData> m_archetypeSortData;
80 cnt::darray<GroupData> m_archetypeGroupData;
83 ArchetypeId m_lastArchetypeId{};
85 uint32_t m_worldVersion{};
87 enum QueryCmdType : uint8_t { ALL, ANY, NOT };
89 template <
typename TType>
90 GAIA_NODISCARD
bool has_inter([[maybe_unused]] QueryOpKind op,
bool isReadWrite)
const {
91 using T = core::raw_t<TType>;
93 if constexpr (std::is_same_v<T, Entity>) {
95 GAIA_ASSERT(!isReadWrite);
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);
106 id = m_ctx.cc->get<T>().entity;
109 const auto& ctxData = m_ctx.data;
110 const auto compIdx = comp_idx<MAX_ITEMS_IN_QUERY>(ctxData._terms.data(),
id, EntityBad);
112 if (op != ctxData._terms[compIdx].op)
116 const uint32_t maskRW = (uint32_t)ctxData.readWriteMask & (1U << compIdx);
117 const uint32_t maskXX = (uint32_t)isReadWrite << compIdx;
118 return maskRW == maskXX;
122 template <
typename T>
123 GAIA_NODISCARD
bool has_inter(QueryOpKind op)
const {
125 constexpr bool isReadWrite = core::is_mut_v<T>;
126 return has_inter<T>(op, isReadWrite);
132 GAIA_ASSERT(m_refs != 0);
136 GAIA_ASSERT(m_refs > 0);
140 uint32_t refs()
const {
144 void init(World* world) {
150 m_archetypeCache = {};
151 m_archetypeSortData = {};
152 m_archetypeCacheData = {};
153 m_archetypeGroupData = {};
154 m_lastArchetypeId = 0;
156 m_ctx.data.lastMatchedArchetypeIdx_All = {};
157 m_ctx.data.lastMatchedArchetypeIdx_Any = {};
158 m_ctx.data.lastMatchedArchetypeIdx_Not = {};
161 GAIA_NODISCARD
static QueryInfo create(
162 QueryId
id, QueryCtx&& ctx,
const EntityToArchetypeMap& entityToArchetypeMap,
163 const ArchetypeDArray& allArchetypes) {
171 info.m_ctx = GAIA_MOV(ctx);
172 info.m_ctx.q.handle = {id, 0};
175 info.compile(entityToArchetypeMap, allArchetypes);
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;
193 info.m_ctx = GAIA_MOV(queryCtx);
194 info.m_ctx.q.handle = {
idx, gen};
197 info.compile(entityToArchetypeMap, allArchetypes);
202 GAIA_NODISCARD
static QueryHandle handle(
const QueryInfo& info) {
203 return QueryHandle(info.idx, info.data.gen);
208 GAIA_PROF_SCOPE(queryinfo::compile);
211 m_vm.compile(entityToArchetypeMap, allArchetypes, m_ctx);
216 GAIA_PROF_SCOPE(queryinfo::recompile);
219 m_vm.create_opcodes(m_ctx);
222 void set_world_version(uint32_t version) {
223 m_worldVersion = version;
226 GAIA_NODISCARD uint32_t world_version()
const {
227 return m_worldVersion;
230 GAIA_NODISCARD
bool operator==(
const QueryCtx& other)
const {
231 return m_ctx == other;
234 GAIA_NODISCARD
bool operator!=(
const QueryCtx& other)
const {
235 return m_ctx != other;
250 ArchetypeId archetypeLastId) {
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;
263 ~CleanUpTmpArchetypeMatches() {
267 s_tmpArchetypeMatchesSet.clear();
268 s_tmpArchetypeMatchesArr.clear();
272 auto& ctxData = m_ctx.data;
275 if ((ctxData.flags & QueryCtx::QueryFlags::Recompile) != 0)
279 if (!m_vm.is_compiled())
283 GAIA_ASSERT(archetypeLastId >= m_lastArchetypeId);
284 if (m_lastArchetypeId == archetypeLastId) {
290 m_lastArchetypeId = archetypeLastId;
292 GAIA_PROF_SCOPE(queryinfo::match);
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;
313 for (
auto* pArchetype: *ctx.pMatchesArr)
314 add_archetype_to_cache(pArchetype);
325 GAIA_PROF_SCOPE(queryinfo::calc_sort_data);
327 m_archetypeSortData.clear();
345 uint32_t chunkIdx = 0;
349 auto& archetypes = m_archetypeCache;
354 uint32_t currArchetypeIdx = (uint32_t)-1;
355 Chunk* pCurrentChunk =
nullptr;
356 uint16_t currentStartRow = 0;
357 uint16_t currentRow = 0;
359 const void* pDataMin =
nullptr;
360 const void* pDataCurr =
nullptr;
363 uint32_t minArchetypeIdx = (uint32_t)-1;
364 Entity minEntity = EntityBad;
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];
372 while (cur.chunkIdx < chunks.size() && cur.row >= chunks[cur.chunkIdx]->size()) {
377 if (cur.chunkIdx >= chunks.size())
380 const auto* pChunk = pArchetype->chunks()[cur.chunkIdx];
381 auto entity = pChunk->entity_view()[cur.row];
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);
387 pDataCurr = &pChunk->entity_view()[cur.row];
389 if (minEntity == EntityBad) {
392 pDataMin = pDataCurr;
396 if (m_ctx.data.sortByFunc(*m_ctx.w, pDataCurr, pDataMin) < 0) {
403 if (minArchetypeIdx == (uint32_t)-1)
406 auto& cur = cursors[minArchetypeIdx];
407 const auto& chunks = archetypes[minArchetypeIdx]->chunks();
408 Chunk* pChunk = chunks[cur.chunkIdx];
410 if (minArchetypeIdx == currArchetypeIdx && pChunk == pCurrentChunk) {
414 if (pCurrentChunk !=
nullptr) {
415 m_archetypeSortData.push_back(
416 {pCurrentChunk, currArchetypeIdx, currentStartRow, (uint16_t)(currentRow - currentStartRow)});
420 currArchetypeIdx = minArchetypeIdx;
421 pCurrentChunk = pChunk;
422 currentStartRow = cur.row;
426 currentRow = cur.row;
429 if (pCurrentChunk !=
nullptr) {
430 m_archetypeSortData.push_back(
431 {pCurrentChunk, currArchetypeIdx, currentStartRow, (uint16_t)(currentRow - currentStartRow)});
435 void sort_entities() {
436 if (m_ctx.data.sortByFunc ==
nullptr)
439 if ((m_ctx.data.flags & QueryCtx::QueryFlags::SortEntities) == 0) {
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)) {
456 m_ctx.
data.flags ^= QueryCtx::QueryFlags::SortEntities;
459 for (
auto* pArchetype: m_archetypeCache)
460 pArchetype->sort_entities(m_ctx.
data.sortBy, m_ctx.
data.sortByFunc);
466 void sort_cache_groups() {
467 if ((m_ctx.data.flags & QueryCtx::QueryFlags::SortGroups) == 0)
469 m_ctx.data.flags ^= QueryCtx::QueryFlags::SortGroups;
472 bool operator()(
const ArchetypeCacheData& a,
const ArchetypeCacheData& b)
const {
473 return a.groupId <= b.groupId;
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;
487 auto tmp = m_archetypeCacheData[left];
488 m_archetypeCacheData[left] = m_archetypeCacheData[right];
489 m_archetypeCacheData[right] = tmp;
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();
498 const auto idxBeforeRemapping = m_ctx.data._remapping[i];
499 const auto queryId = queryIds[idxBeforeRemapping];
502 const auto compIdx = core::get_index_unsafe(pArchetype->ids_view(), queryId);
503 GAIA_ASSERT(compIdx != BadIndex);
505 cacheData.indices[i] = (uint8_t)compIdx;
510 void add_archetype_to_cache_no_grouping(Archetype* pArchetype) {
511 GAIA_PROF_SCOPE(queryinfo::add_cache_ng);
513 if (m_archetypeSet.contains(pArchetype))
516 m_archetypeSet.emplace(pArchetype);
517 m_archetypeCache.push_back(pArchetype);
518 m_archetypeCacheData.push_back(create_cache_data(pArchetype));
521 void add_archetype_to_cache_w_grouping(Archetype* pArchetype) {
522 GAIA_PROF_SCOPE(queryinfo::add_cache_wg);
524 if (m_archetypeSet.contains(pArchetype))
527 const GroupId groupId = m_ctx.data.groupByFunc(*m_ctx.w, *pArchetype, m_ctx.data.groupBy);
529 ArchetypeCacheData cacheData = create_cache_data(pArchetype);
530 cacheData.groupId = groupId;
532 if (m_archetypeGroupData.empty()) {
533 m_archetypeGroupData.push_back({groupId, 0, 0,
false});
535 const auto cnt = m_archetypeGroupData.size();
537 if (groupId < m_archetypeGroupData[i].groupId) {
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();
548 for (uint32_t j = i + 1; j < lastGrpIdx; ++j) {
549 ++m_archetypeGroupData[j].idxFirst;
550 ++m_archetypeGroupData[j].idxLast;
554 m_ctx.data.flags |= QueryCtx::QueryFlags::SortGroups;
556 }
else if (m_archetypeGroupData[i].groupId == groupId) {
557 const auto lastGrpIdx = m_archetypeGroupData.size();
558 ++m_archetypeGroupData[i].idxLast;
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;
573 const auto groupsCnt = m_archetypeGroupData.size();
574 if (groupsCnt == 0) {
576 m_archetypeGroupData.push_back(
577 {groupId, 0, 0,
false});
579 const auto& groupPrev = m_archetypeGroupData[groupsCnt - 1];
580 GAIA_ASSERT(groupPrev.idxLast + 1 == m_archetypeCache.size());
582 m_archetypeGroupData.push_back(
584 groupPrev.idxLast + 1,
585 groupPrev.idxLast + 1,
593 m_archetypeSet.emplace(pArchetype);
594 m_archetypeCache.push_back(pArchetype);
595 m_archetypeCacheData.push_back(GAIA_MOV(cacheData));
598 void add_archetype_to_cache(Archetype* pArchetype) {
599 if (m_ctx.data.sortByFunc !=
nullptr)
600 m_ctx.data.flags |= QueryCtx::QueryFlags::SortEntities;
602 if (m_ctx.data.groupBy != EntityBad)
603 add_archetype_to_cache_w_grouping(pArchetype);
605 add_archetype_to_cache_no_grouping(pArchetype);
608 bool del_archetype_from_cache(Archetype* pArchetype) {
609 const auto it = m_archetypeSet.find(pArchetype);
610 if (it == m_archetypeSet.end())
612 m_archetypeSet.erase(it);
614 if (m_ctx.data.sortByFunc !=
nullptr)
615 m_ctx.data.flags |= QueryCtx::QueryFlags::SortEntities;
617 const auto archetypeIdx = core::get_index_unsafe(m_archetypeCache, pArchetype);
618 GAIA_ASSERT(archetypeIdx != BadIndex);
620 core::swap_erase(m_archetypeCache, archetypeIdx);
621 core::swap_erase(m_archetypeCacheData, archetypeIdx);
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;
629 GAIA_ASSERT(grpIdx != BadIndex);
631 auto& currGrp = m_archetypeGroupData[archetypeIdx];
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;
641 if (currGrp.idxLast - currGrp.idxFirst > 0)
644 m_archetypeGroupData.erase(m_archetypeGroupData.begin() + grpIdx);
650 GAIA_NODISCARD World* world() {
651 GAIA_ASSERT(m_ctx.w !=
nullptr);
652 return const_cast<World*
>(m_ctx.w);
654 GAIA_NODISCARD
const World* world()
const {
655 GAIA_ASSERT(m_ctx.w !=
nullptr);
659 GAIA_NODISCARD QuerySerBuffer& ser_buffer() {
660 return m_ctx.q.ser_buffer(world());
662 void ser_buffer_reset() {
663 m_ctx.q.ser_buffer_reset(world());
666 GAIA_NODISCARD QueryCtx& ctx() {
669 GAIA_NODISCARD
const QueryCtx& ctx()
const {
673 GAIA_NODISCARD
bool has_filters()
const {
674 return m_ctx.data.changedCnt > 0;
677 template <
typename... T>
678 bool has_any()
const {
679 return (has_inter<T>(QueryOpKind::Any) || ...);
682 template <
typename... T>
683 bool has_all()
const {
684 return (has_inter<T>(QueryOpKind::All) && ...);
687 template <
typename... T>
688 bool has_no()
const {
689 return (!has_inter<T>(QueryOpKind::Not) && ...);
695 GAIA_PROF_SCOPE(queryinfo::remove);
697 if (!del_archetype_from_cache(pArchetype))
703 for (
auto&
pair: matches) {
704 auto& lastMatchedArchetypeIdx =
pair.second;
705 if (lastMatchedArchetypeIdx > 0)
706 --lastMatchedArchetypeIdx;
709 clearMatches(m_ctx.data.lastMatchedArchetypeIdx_All);
710 clearMatches(m_ctx.data.lastMatchedArchetypeIdx_Any);
715 const auto& ctxData = m_archetypeCacheData[archetypeIdx];
719 GAIA_NODISCARD ArchetypeDArray::iterator begin() {
720 return m_archetypeCache.begin();
723 GAIA_NODISCARD ArchetypeDArray::const_iterator begin()
const {
724 return m_archetypeCache.begin();
727 GAIA_NODISCARD ArchetypeDArray::const_iterator cbegin()
const {
728 return m_archetypeCache.begin();
731 GAIA_NODISCARD ArchetypeDArray::iterator end() {
732 return m_archetypeCache.end();
735 GAIA_NODISCARD ArchetypeDArray::const_iterator end()
const {
736 return m_archetypeCache.end();
739 GAIA_NODISCARD ArchetypeDArray::const_iterator cend()
const {
740 return m_archetypeCache.end();
744 return std::span{(
const Archetype**)m_archetypeCache.data(), m_archetypeCache.size()};
748 return std::span{m_archetypeCacheData.data(), m_archetypeCacheData.size()};
752 return std::span{m_archetypeSortData.data(), m_archetypeSortData.size()};
756 return std::span{m_archetypeGroupData.data(), m_archetypeGroupData.size()};