63 enum class ChangeKind : uint8_t {
75 add_entity_to_query_pairs(info.
ctx().data.ids_view(), handle);
76 add_rel_to_query_pairs(info.
ctx(), handle);
77 add_sort_to_query_pairs(info.
ctx(), handle);
78 add_sorted_query(info.
ctx(), handle);
79 add_create_to_query_pairs(info.
ctx(), handle);
86 struct CreateQueryCandidate {
88 Entity matchedSelector = EntityBad;
93 enum class CreateSelectorKind : uint8_t {
102 struct TrackedArchetypes {
104 uint32_t syncedRevision = 0;
129 uint32_t m_createQueryHandleStamp = 1;
131 uint32_t m_createQuerySelectorCnt[(size_t)CreateSelectorKind::Count] = {};
135 m_queryArr.reserve(256);
145 GAIA_NODISCARD
bool valid(
QueryHandle handle)
const {
146 if (handle.id() == QueryIdBad)
149 if (!m_queryArr.has(handle.id()))
152 const auto& h = m_queryArr[handle.id()];
153 return h.idx == handle.id() && h.gen == handle.gen();
159 m_entityToQuery.clear();
160 m_relationToQuery.clear();
161 m_sortEntityToQuery.clear();
162 m_sortedQueries.clear();
163 m_entityToCreateQuery.clear();
164 m_archetypeToQuery.clear();
165 m_queryToArchetype.clear();
166 m_createQueryHandleScratch.clear();
167 m_createQueryHandleStampById.clear();
168 m_createQueryHandleStamp = 1;
169 for (
auto& cnt: m_createQuerySelectorCnt)
176 m_archetypeToQuery.clear();
177 m_queryToArchetype.clear();
187 auto& info = m_queryArr[handle.id()];
188 GAIA_ASSERT(info.
idx == handle.id());
189 GAIA_ASSERT(info.
gen == handle.gen());
200 const auto& info = m_queryArr[handle.id()];
201 GAIA_ASSERT(info.
idx == handle.id());
202 GAIA_ASSERT(info.
gen == handle.gen());
206#if GAIA_ECS_TEST_HOOKS
208 GAIA_NODISCARD
bool verify_archetype_tracking()
const {
209 for (
const auto&
pair: m_queryToArchetype) {
210 const auto handle =
pair.first.handle();
211 const auto* pInfo =
try_get(handle);
212 if (pInfo ==
nullptr || pInfo->refs() == 0)
215 const auto& tracked =
pair.second.archetypes;
216 for (uint32_t i = 0; i < tracked.size(); ++i) {
217 const auto* pArchetype = tracked[i];
218 if (pArchetype ==
nullptr)
221 for (uint32_t j = i + 1; j < tracked.size(); ++j) {
222 if (tracked[j] == pArchetype)
226 if (!archetype_span_contains(pInfo->cache_archetype_view(), pArchetype))
229 const auto archetypeKey = ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash());
230 const auto reverseIt = m_archetypeToQuery.find(archetypeKey);
231 if (reverseIt == m_archetypeToQuery.end() || !core::has(reverseIt->second, handle))
236 for (
const auto& pair: m_archetypeToQuery) {
237 const auto& handles = pair.second;
238 for (uint32_t i = 0; i < handles.size(); ++i) {
239 const auto handle = handles[i];
240 const auto* pInfo =
try_get(handle);
241 if (pInfo ==
nullptr || pInfo->refs() == 0)
244 for (uint32_t j = i + 1; j < handles.size(); ++j) {
245 if (handles[j] == handle)
249 const auto trackedIt = m_queryToArchetype.find(QueryHandleLookupKey(handle));
250 if (trackedIt == m_queryToArchetype.end())
253 if (!tracked_archetypes_contain(trackedIt->second.archetypes, pair.first))
263 GAIA_NODISCARD uint32_t test_query_count()
const {
264 return (uint32_t)m_queryArr.item_count();
272 GAIA_ASSERT(valid(handle));
274 auto& info = m_queryArr[handle.id()];
275 GAIA_ASSERT(info.
idx == handle.id());
276 GAIA_ASSERT(info.
gen == handle.gen());
289 GAIA_ASSERT(ctx.hashLookup.hash != 0);
292 auto ret = m_pCache.try_emplace(
QueryLookupKey(ctx.hashLookup, &ctx),
nullptr);
294 auto* pInfo = ret.first->second;
295 GAIA_ASSERT(pInfo !=
nullptr);
302 creationCtx.pQueryCtx = &ctx;
303 creationCtx.pEntityToArchetypeMap = &entityToArchetypeMap;
304 creationCtx.allArchetypes = allArchetypes;
305 auto handle = m_queryArr.alloc(&creationCtx);
309 auto& info =
get(handle);
311 ret.first->second = &info;
313 ret.first->swap(new_p);
315 return register_query_info(handle, info);
324 creationCtx.pQueryCtx = &ctx;
325 creationCtx.pEntityToArchetypeMap = &entityToArchetypeMap;
326 creationCtx.allArchetypes = allArchetypes;
327 auto handle = m_queryArr.alloc(&creationCtx);
329 auto& info =
get(handle);
331 return register_query_info(handle, info);
339 if (pInfo ==
nullptr)
343 if (pInfo->refs() != 0)
346 unregister_query_archetypes(handle);
349 auto it = m_pCache.find(
QueryLookupKey(pInfo->ctx().hashLookup, &pInfo->ctx()));
350 if (it != m_pCache.end())
354 del_entity_to_query_pairs(pInfo->ctx().data.ids_view(), handle);
355 del_rel_to_query_pairs(pInfo->ctx(), handle);
356 del_sort_to_query_pairs(pInfo->ctx(), handle);
357 del_sorted_query(pInfo->ctx(), handle);
358 del_create_to_query_pairs(pInfo->ctx(), handle);
359 m_queryArr.free(handle);
365 return m_queryArr.begin();
369 return m_queryArr.end();
380 auto it = m_entityToQuery.find(entityKey);
381 if (it == m_entityToQuery.end())
384 const auto& handles = it->second;
385 for (
const auto& handle: handles) {
386 auto& info =
get(handle);
389 info.
invalidate(select_invalidation_kind(info, changeKind));
393 void invalidate_queries_for_rel(
Entity relation, ChangeKind changeKind) {
395 if (it == m_relationToQuery.end())
398 for (
const auto handle: it->second) {
399 auto& info =
get(handle);
401 info.
invalidate(select_invalidation_kind(info, changeKind));
405 void invalidate_sorted_queries_for_entity(Entity entity) {
406 auto it = m_sortEntityToQuery.find(EntityLookupKey(entity));
407 if (it == m_sortEntityToQuery.end())
410 for (
const auto handle: it->second) {
412 if (pInfo ==
nullptr || pInfo->refs() == 0)
415 pInfo->invalidate_sort();
421 for (
const auto handle: m_sortedQueries) {
423 if (pInfo ==
nullptr || pInfo->refs() == 0)
426 pInfo->invalidate_sort();
430 void sync_archetype_cache(
QueryInfo& queryInfo) {
437 auto it = m_queryToArchetype.find(key);
438 if (it != m_queryToArchetype.end() && it->second.syncedRevision == queryInfo.
result_cache_rev())
441 unregister_query_archetypes(handle);
443 if (archetypes.empty())
446 auto [trackedIt, inserted] = m_queryToArchetype.try_emplace(key);
447 auto& tracked = trackedIt->second.archetypes;
451 tracked.reserve((uint32_t)archetypes.size());
452 for (
const auto* pArchetype: archetypes) {
453 tracked.push_back(pArchetype);
454 add_archetype_query_pair(pArchetype, handle);
459 void remove_archetype_from_queries(Archetype* pArchetype) {
460 const auto archetypeKey = ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash());
461 auto it = m_archetypeToQuery.find(archetypeKey);
462 if (it == m_archetypeToQuery.end())
465 const auto handles = it->second;
466 for (
const auto handle: handles) {
468 if (pInfo !=
nullptr && pInfo->refs() != 0)
469 pInfo->remove(pArchetype);
471 auto trackedIt = m_queryToArchetype.find(QueryHandleLookupKey(handle));
472 if (trackedIt == m_queryToArchetype.end())
475 auto& tracked = trackedIt->second.archetypes;
476 core::swap_erase(tracked, core::get_index(tracked, pArchetype));
478 m_queryToArchetype.
erase(trackedIt);
481 m_archetypeToQuery.
erase(it);
484 void register_archetype_with_queries(
const Archetype* pArchetype) {
485 auto& handles = prepare_create_query_handles();
486 const bool needsExactPairSelectors = has_create_selector_kind(CreateSelectorKind::ExactPair);
487 const bool needsRelWildcardSelectors = has_create_selector_kind(CreateSelectorKind::RelWildcardPair);
488 const bool needsTgtWildcardSelectors = has_create_selector_kind(CreateSelectorKind::TgtWildcardPair);
489 const bool needsAnyPairWildcardSelectors = has_create_selector_kind(CreateSelectorKind::AnyPairWildcard);
490 bool hasAnyPair =
false;
491 cnt::darray_ext<Entity, 16> pairWildcardRelations;
492 for (
const auto entity: pArchetype->ids_view()) {
493 if (!entity.pair()) {
494 add_create_query_handles(entity, handles);
499 if (needsExactPairSelectors)
500 add_create_query_handles(entity, handles);
504 const auto relKind = entity.entity() ? EntityKind::EK_Uni : EntityKind::EK_Gen;
505 const auto rel = Entity((EntityId)entity.id(), 0,
false,
false, relKind);
506 const auto tgt = Entity((EntityId)entity.gen(), 0,
false,
false, entity.kind());
507 if (needsTgtWildcardSelectors)
508 add_create_query_handles(Pair(All, tgt), handles);
509 if (needsRelWildcardSelectors && !core::has(pairWildcardRelations, rel)) {
510 pairWildcardRelations.push_back(rel);
511 add_create_query_handles(Pair(rel, All), handles);
515 if (hasAnyPair && needsAnyPairWildcardSelectors)
516 add_create_query_handles(Pair(All, All), handles);
518 for (
const auto& candidate: handles) {
519 auto* pInfo =
try_get(candidate.handle);
520 if (pInfo ==
nullptr || pInfo->refs() == 0)
523 if (!pInfo->register_archetype(*pArchetype, candidate.matchedSelector,
true))
526 register_query_archetype(candidate.handle, pArchetype, pInfo->result_cache_rev());
531#if GAIA_ECS_TEST_HOOKS
532 GAIA_NODISCARD
static bool
534 for (
const auto* pCachedArchetype: archetypes) {
535 if (pCachedArchetype == pArchetype)
541 GAIA_NODISCARD
static bool tracked_archetypes_contain(
542 const cnt::darray<const Archetype*>& archetypes,
const ArchetypeIdLookupKey& archetypeKey) {
543 for (
const auto* pArchetype: archetypes) {
544 if (pArchetype ==
nullptr)
547 if (ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()) == archetypeKey)
555 static CreateSelectorKind classify_create_selector(Entity entity) {
557 return CreateSelectorKind::Other;
558 if (is_wildcard(entity.id()))
559 return is_wildcard(entity.gen()) ? CreateSelectorKind::AnyPairWildcard : CreateSelectorKind::TgtWildcardPair;
560 if (is_wildcard(entity.gen()))
561 return CreateSelectorKind::RelWildcardPair;
562 return CreateSelectorKind::ExactPair;
565 GAIA_NODISCARD
static constexpr uint32_t selector_kind_idx(CreateSelectorKind kind) {
566 return (uint32_t)kind;
569 GAIA_NODISCARD
bool has_create_selector_kind(CreateSelectorKind kind)
const {
570 return m_createQuerySelectorCnt[selector_kind_idx(kind)] != 0;
574 void track_create_selector(Entity entity) {
575 const auto kind = classify_create_selector(entity);
576 ++m_createQuerySelectorCnt[selector_kind_idx(kind)];
580 void untrack_create_selector(Entity entity) {
581 const auto kind = classify_create_selector(entity);
582 auto& cnt = m_createQuerySelectorCnt[selector_kind_idx(kind)];
583 GAIA_ASSERT(cnt != 0);
588 switch (changeKind) {
589 case ChangeKind::DynamicResult:
591 case ChangeKind::All:
593 case ChangeKind::Structural:
597 return (info.ctx().data.deps.has_dep_flag(QueryCtx::DependencyHasSourceTerms) ||
598 info.ctx().data.deps.has_dep_flag(QueryCtx::DependencyHasVariableTerms))
610 void add_entity_query_pair(Entity entity, QueryHandle handle) {
611 EntityLookupKey entityKey(entity);
612 const auto it = m_entityToQuery.find(entityKey);
613 if (it == m_entityToQuery.end()) {
614 m_entityToQuery.try_emplace(entityKey, cnt::darray<QueryHandle>{handle});
618 auto& handles = it->second;
619 if (!core::has(handles, handle))
620 handles.push_back(handle);
626 void del_entity_query_pair(Entity entity, QueryHandle handle) {
627 auto it = m_entityToQuery.find(EntityLookupKey(entity));
628 if (it == m_entityToQuery.end())
631 auto& handles = it->second;
632 const auto idx = core::get_index_unsafe(handles, handle);
633 core::swap_erase_unsafe(handles, idx);
637 m_entityToQuery.
erase(it);
643 void add_entity_to_query_pairs(EntitySpan entities, QueryHandle handle) {
644 for (
auto entity: entities) {
645 add_entity_query_pair(entity, handle);
652 void del_entity_to_query_pairs(EntitySpan entities, QueryHandle handle) {
653 for (
auto entity: entities) {
654 del_entity_query_pair(entity, handle);
658 void add_create_to_query_pair(Entity entity, QueryHandle handle) {
659 EntityLookupKey entityKey(entity);
660 const auto it = m_entityToCreateQuery.find(entityKey);
661 if (it == m_entityToCreateQuery.end()) {
662 m_entityToCreateQuery.try_emplace(entityKey, cnt::darray<QueryHandle>{handle});
663 track_create_selector(entity);
667 auto& handles = it->second;
668 if (!core::has(handles, handle)) {
669 handles.push_back(handle);
670 track_create_selector(entity);
674 void add_sort_to_query_pair(Entity entity, QueryHandle handle) {
675 auto it = m_sortEntityToQuery.find(EntityLookupKey(entity));
676 if (it == m_sortEntityToQuery.end()) {
677 m_sortEntityToQuery.try_emplace(EntityLookupKey(entity), cnt::darray<QueryHandle>{handle});
681 auto& handles = it->second;
682 if (!core::has(handles, handle))
683 handles.push_back(handle);
686 void del_sort_to_query_pair(Entity entity, QueryHandle handle) {
687 auto it = m_sortEntityToQuery.find(EntityLookupKey(entity));
688 if (it == m_sortEntityToQuery.end())
691 auto& handles = it->second;
692 core::swap_erase(handles, core::get_index(handles, handle));
694 m_sortEntityToQuery.
erase(it);
697 void add_sort_to_query_pairs(
const QueryCtx& ctx, QueryHandle handle) {
698 if (ctx.data.sortByFunc ==
nullptr || ctx.data.sortBy == EntityBad)
701 add_sort_to_query_pair(ctx.data.sortBy, handle);
704 void del_sort_to_query_pairs(
const QueryCtx& ctx, QueryHandle handle) {
705 if (ctx.data.sortByFunc ==
nullptr || ctx.data.sortBy == EntityBad)
708 del_sort_to_query_pair(ctx.data.sortBy, handle);
711 void add_sorted_query(
const QueryCtx& ctx, QueryHandle handle) {
712 if (ctx.data.sortByFunc ==
nullptr)
715 m_sortedQueries.push_back(handle);
718 void del_sorted_query(
const QueryCtx& ctx, QueryHandle handle) {
719 if (ctx.data.sortByFunc ==
nullptr)
722 const auto idx = core::get_index(m_sortedQueries, handle);
725 core::swap_erase(m_sortedQueries, idx);
728 void del_create_to_query_pair(Entity entity, QueryHandle handle) {
729 auto it = m_entityToCreateQuery.find(EntityLookupKey(entity));
730 if (it == m_entityToCreateQuery.end())
733 auto& handles = it->second;
734 core::swap_erase(handles, core::get_index(handles, handle));
735 untrack_create_selector(entity);
737 m_entityToCreateQuery.
erase(it);
740 void add_create_to_query_pairs(
const QueryCtx& ctx, QueryHandle handle) {
741 if (ctx.data.cachePolicy != QueryCtx::CachePolicy::Immediate)
747 for (
const auto entity: ctx.data.deps.create_selectors_view())
748 add_create_to_query_pair(entity, handle);
751 void del_create_to_query_pairs(
const QueryCtx& ctx, QueryHandle handle) {
752 if (ctx.data.cachePolicy != QueryCtx::CachePolicy::Immediate)
755 for (
const auto entity: ctx.data.deps.create_selectors_view())
756 del_create_to_query_pair(entity, handle);
759 void add_create_query_handles(Entity selector, cnt::darray<CreateQueryCandidate>& handles) {
760 const auto it = m_entityToCreateQuery.find(EntityLookupKey(selector));
761 if (it == m_entityToCreateQuery.end())
764 for (
const auto handle: it->second) {
765 if (mark_create_query_handle(handle))
766 handles.push_back(CreateQueryCandidate{handle, selector});
770 cnt::darray<CreateQueryCandidate>& prepare_create_query_handles() {
771 m_createQueryHandleScratch.clear();
775 ++m_createQueryHandleStamp;
776 if (m_createQueryHandleStamp == 0) {
777 m_createQueryHandleStampById = {};
778 m_createQueryHandleStamp = 1;
781 return m_createQueryHandleScratch;
784 GAIA_NODISCARD
bool mark_create_query_handle(QueryHandle handle) {
785 const auto handleId = (uint32_t)handle.id();
786 if (handleId >= m_createQueryHandleStampById.size())
787 m_createQueryHandleStampById.resize(handleId + 1);
789 auto& stamp = m_createQueryHandleStampById[handleId];
790 if (stamp == m_createQueryHandleStamp)
793 stamp = m_createQueryHandleStamp;
797 void add_archetype_query_pair(
const Archetype* pArchetype, QueryHandle handle) {
798 const auto archetypeKey = ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash());
799 const auto it = m_archetypeToQuery.find(archetypeKey);
800 if (it == m_archetypeToQuery.end()) {
801 m_archetypeToQuery.try_emplace(archetypeKey, cnt::darray<QueryHandle>{handle});
805 auto& handles = it->second;
807 GAIA_ASSERT(!core::has(handles, handle));
808 handles.push_back(handle);
811 void del_archetype_query_pair(
const Archetype* pArchetype, QueryHandle handle) {
812 auto it = m_archetypeToQuery.find(ArchetypeIdLookupKey(pArchetype->id(), pArchetype->id_hash()));
813 if (it == m_archetypeToQuery.end())
816 auto& handles = it->second;
817 const auto idx = core::get_index(handles, handle);
819 core::swap_erase(handles, idx);
821 m_archetypeToQuery.
erase(it);
824 void unregister_query_archetypes(QueryHandle handle) {
825 auto it = m_queryToArchetype.find(QueryHandleLookupKey(handle));
826 if (it == m_queryToArchetype.end())
829 const auto& tracked = it->second.archetypes;
830 for (
const auto* pArchetype: tracked)
831 del_archetype_query_pair(pArchetype, handle);
833 m_queryToArchetype.
erase(it);
836 void register_query_archetype(QueryHandle handle,
const Archetype* pArchetype, uint32_t syncedRevision) {
837 auto [trackedIt, inserted] = m_queryToArchetype.try_emplace(QueryHandleLookupKey(handle));
838 auto& tracked = trackedIt->second.archetypes;
842 GAIA_ASSERT(inserted || !core::has(tracked, pArchetype));
843 tracked.push_back(pArchetype);
844 trackedIt->second.syncedRevision = syncedRevision;
845 add_archetype_query_pair(pArchetype, handle);
848 void add_rel_query_pair(Entity relation, QueryHandle handle) {
849 const auto key = EntityLookupKey(relation);
850 const auto it = m_relationToQuery.find(key);
851 if (it == m_relationToQuery.end()) {
852 m_relationToQuery.try_emplace(key, cnt::darray<QueryHandle>{handle});
856 auto& handles = it->second;
857 if (!core::has(handles, handle))
858 handles.push_back(handle);
861 void del_rel_query_pair(Entity relation, QueryHandle handle) {
862 auto it = m_relationToQuery.find(EntityLookupKey(relation));
863 if (it == m_relationToQuery.end())
866 auto& handles = it->second;
867 core::swap_erase(handles, core::get_index(handles, handle));
869 m_relationToQuery.
erase(it);
872 void add_rel_to_query_pairs(
const QueryCtx& ctx, QueryHandle handle) {
873 for (
const auto relation: ctx.data.deps.relations_view())
874 add_rel_query_pair(relation, handle);
877 void del_rel_to_query_pairs(
const QueryCtx& ctx, QueryHandle handle) {
878 for (
const auto relation: ctx.data.deps.relations_view())
879 del_rel_query_pair(relation, handle);