359 static constexpr uint32_t ChunkBatchSize = 32;
364 const uint8_t* pIndicesMapping;
376 static constexpr CmdFunc CommandBufferRead[] = {
380 ser::load(buffer, cmd);
386 ser::load(buffer, cmd);
392 ser::load(buffer, cmd);
398 ser::load(buffer, cmd);
404 ser::load(buffer, cmd);
412 ArchetypeId* m_nextArchetypeId{};
414 uint32_t* m_worldVersion{};
430 if constexpr (UseCaching) {
431 GAIA_PROF_SCOPE(query::fetch);
438 if GAIA_LIKELY (m_storage.
m_q.
handle.id() != QueryIdBad) {
442 if GAIA_LIKELY (pQueryInfo !=
nullptr) {
443 recommit(pQueryInfo->ctx());
444 pQueryInfo->match(*m_entityToArchetypeMap, *m_allArchetypes, last_archetype_id());
453 ctx.init(m_storage.world());
455 auto& queryInfo = m_storage.
m_queryCache->
add(GAIA_MOV(ctx), *m_entityToArchetypeMap, *m_allArchetypes);
456 m_storage.
m_q.
handle = QueryInfo::handle(queryInfo);
457 m_storage.allow_to_destroy_again();
458 queryInfo.match(*m_entityToArchetypeMap, *m_allArchetypes, last_archetype_id());
461 GAIA_PROF_SCOPE(query::fetchu);
463 if GAIA_UNLIKELY (m_storage.m_queryInfo.ctx().q.handle.id() == QueryIdBad) {
465 ctx.init(m_storage.world());
467 m_storage.m_queryInfo =
468 QueryInfo::create(QueryId{}, GAIA_MOV(ctx), *m_entityToArchetypeMap, *m_allArchetypes);
470 m_storage.m_queryInfo.match(*m_entityToArchetypeMap, *m_allArchetypes, last_archetype_id());
471 return m_storage.m_queryInfo;
477 ArchetypeId last_archetype_id()
const {
478 return *m_nextArchetypeId - 1;
481 template <
typename T>
482 void add_cmd(T& cmd) {
484 if constexpr (T::InvalidatesHash)
487 auto& serBuffer = m_storage.ser_buffer();
488 ser::save(serBuffer, T::Id);
489 ser::save(serBuffer, T::InvalidatesHash);
490 ser::save(serBuffer, cmd);
493 void add_inter(QueryInput item) {
495 GAIA_ASSERT(item.op != QueryOpKind::Not || item.access == QueryAccess::None);
497 QueryCmd_AddItem cmd{item};
501 template <
typename T>
502 void add_inter(QueryOpKind op) {
505 if constexpr (is_pair<T>::value) {
507 const auto& desc_rel = comp_cache_add<typename T::rel_type>(*m_storage.world());
508 const auto& desc_tgt = comp_cache_add<typename T::tgt_type>(*m_storage.world());
510 e = Pair(desc_rel.entity, desc_tgt.entity);
513 const auto& desc = comp_cache_add<T>(*m_storage.world());
518 QueryAccess access = QueryAccess::None;
519 if (op != QueryOpKind::Not) {
520 constexpr auto isReadWrite = core::is_mut_v<T>;
521 access = isReadWrite ? QueryAccess::Write : QueryAccess::Read;
524 add_inter({op, access, e});
527 template <
typename Rel,
typename Tgt>
528 void add_inter(QueryOpKind op) {
529 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
530 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
531 static_assert(core::is_raw_v<UO_Rel>,
"Use add() with raw types only");
532 static_assert(core::is_raw_v<UO_Tgt>,
"Use add() with raw types only");
535 const auto& descRel = comp_cache_add<Rel>(*m_storage.world());
536 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.world());
539 QueryAccess access = QueryAccess::None;
540 if (op != QueryOpKind::Not) {
541 constexpr auto isReadWrite = core::is_mut_v<UO_Rel> || core::is_mut_v<UO_Tgt>;
542 access = isReadWrite ? QueryAccess::Write : QueryAccess::Read;
545 add_inter({op, access, {descRel.entity, descTgt.entity}});
550 void changed_inter(Entity entity) {
551 QueryCmd_AddFilter cmd{entity};
555 template <
typename T>
556 void changed_inter() {
557 using UO =
typename component_type_t<T>::TypeOriginal;
558 static_assert(core::is_raw_v<UO>,
"Use changed() with raw types only");
561 const auto& desc = comp_cache_add<T>(*m_storage.world());
562 changed_inter(desc.entity);
565 template <
typename Rel,
typename Tgt>
566 void changed_inter() {
567 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
568 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
569 static_assert(core::is_raw_v<UO_Rel>,
"Use changed() with raw types only");
570 static_assert(core::is_raw_v<UO_Tgt>,
"Use changed() with raw types only");
573 const auto& descRel = comp_cache_add<Rel>(*m_storage.world());
574 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.world());
575 changed_inter({descRel.entity, descTgt.entity});
580 void sort_by_inter(Entity entity, TSortByFunc func) {
581 QueryCmd_SortBy cmd{entity, func};
585 template <
typename T>
586 void sort_by_inter(TSortByFunc func) {
587 using UO =
typename component_type_t<T>::TypeOriginal;
588 if constexpr (std::is_same_v<UO, Entity>) {
589 sort_by_inter(EntityBad, func);
591 static_assert(core::is_raw_v<UO>,
"Use changed() with raw types only");
594 const auto& desc = comp_cache_add<T>(*m_storage.world());
596 sort_by_inter(desc.entity, func);
600 template <
typename Rel,
typename Tgt>
601 void sort_by_inter(TSortByFunc func) {
602 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
603 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
604 static_assert(core::is_raw_v<UO_Rel>,
"Use group_by() with raw types only");
605 static_assert(core::is_raw_v<UO_Tgt>,
"Use group_by() with raw types only");
608 const auto& descRel = comp_cache_add<Rel>(*m_storage.world());
609 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.world());
611 sort_by_inter({descRel.entity, descTgt.entity}, func);
616 void group_by_inter(Entity entity, TGroupByFunc func) {
617 QueryCmd_GroupBy cmd{entity, func};
621 template <
typename T>
622 void group_by_inter(Entity entity, TGroupByFunc func) {
623 using UO =
typename component_type_t<T>::TypeOriginal;
624 static_assert(core::is_raw_v<UO>,
"Use changed() with raw types only");
626 group_by_inter(entity, func);
629 template <
typename Rel,
typename Tgt>
630 void group_by_inter(TGroupByFunc func) {
631 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
632 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
633 static_assert(core::is_raw_v<UO_Rel>,
"Use group_by() with raw types only");
634 static_assert(core::is_raw_v<UO_Tgt>,
"Use group_by() with raw types only");
637 const auto& descRel = comp_cache_add<Rel>(*m_storage.world());
638 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.world());
640 group_by_inter({descRel.entity, descTgt.entity}, func);
645 void set_group_id_inter(GroupId groupId) {
649 QueryCmd_SetGroupId cmd{groupId};
653 void set_group_id_inter(Entity groupId) {
654 set_group_id_inter(groupId.value());
657 template <
typename T>
658 void set_group_id_inter() {
659 using UO =
typename component_type_t<T>::TypeOriginal;
660 static_assert(core::is_raw_v<UO>,
"Use group_id() with raw types only");
663 const auto& desc = comp_cache_add<T>(*m_storage.world());
664 set_group_inter(desc.entity);
669 void commit(QueryCtx& ctx) {
670 GAIA_PROF_SCOPE(query::commit);
672#if GAIA_ASSERT_ENABLED
673 if constexpr (UseCaching) {
674 GAIA_ASSERT(m_storage.
m_q.
handle.id() == QueryIdBad);
676 GAIA_ASSERT(m_storage.m_queryInfo.idx == QueryIdBad);
680 auto& serBuffer = m_storage.ser_buffer();
684 while (serBuffer.tell() < serBuffer.bytes()) {
686 bool invalidatesHash =
false;
687 ser::load(serBuffer,
id);
688 ser::load(serBuffer, invalidatesHash);
689 (void)invalidatesHash;
690 CommandBufferRead[id](serBuffer, ctx);
694 if constexpr (UseCaching) {
695 calc_lookup_hash(ctx);
699 m_storage.ser_buffer_reset();
705 void recommit(QueryCtx& ctx) {
706 GAIA_PROF_SCOPE(query::recommit);
708 auto& serBuffer = m_storage.ser_buffer();
712 while (serBuffer.tell() < serBuffer.bytes()) {
714 bool invalidatesHash =
false;
715 ser::load(serBuffer,
id);
716 ser::load(serBuffer, invalidatesHash);
718 GAIA_ASSERT(!invalidatesHash);
721 CommandBufferRead[id](serBuffer, ctx);
725 m_storage.ser_buffer_reset();
730#if GAIA_ASSERT_ENABLED
732 template <
typename... T>
733 GAIA_NODISCARD
bool unpack_args_into_query_has_all(
734 const QueryInfo& queryInfo, [[maybe_unused]] core::func_type_list<T...> types)
const {
735 if constexpr (
sizeof...(T) > 0)
736 return queryInfo.template has_all<T...>();
744 GAIA_NODISCARD
static bool match_filters(
const Chunk& chunk,
const QueryInfo& queryInfo) {
745 GAIA_ASSERT(!chunk.empty() &&
"match_filters called on an empty chunk");
747 const auto queryVersion = queryInfo.world_version();
748 const auto& filtered = queryInfo.ctx().data.changed_view();
751 if (filtered.empty())
755 uint32_t lastIdx = 0;
756 for (
const auto comp: filtered) {
759 const auto compIdx = chunk.comp_idx(comp, lastIdx);
760 if (chunk.changed(queryVersion, compIdx))
768 return chunk.changed(queryInfo.world_version());
771 GAIA_NODISCARD
bool can_process_archetype(
const Archetype& archetype)
const {
773 return !archetype.is_req_del();
779 template <
typename Func,
typename TIter>
782 it.set_world(pWorld);
783 it.set_archetype(batch.pArchetype);
784 it.set_chunk(batch.pChunk, batch.from, batch.to);
785 it.set_group_id(batch.groupId);
786 it.set_remapping_indices(batch.pIndicesMapping);
791 template <
typename Func,
typename TIter>
793 GAIA_PROF_SCOPE(query::run_query_func);
795 const auto chunkCnt = batches.size();
796 GAIA_ASSERT(chunkCnt > 0);
799 if GAIA_UNLIKELY (chunkCnt == 1) {
800 run_query_func<Func, TIter>(pWorld, func, batches[0]);
813 run_query_func<Func, TIter>(pWorld, func, batches[0]);
815 uint32_t chunkIdx = 1;
816 for (; chunkIdx < chunkCnt - 1; ++chunkIdx) {
818 run_query_func<Func, TIter>(pWorld, func, batches[chunkIdx]);
821 run_query_func<Func, TIter>(pWorld, func, batches[chunkIdx]);
824 template <
bool HasFilters,
typename TIter,
typename Func>
825 void run_query_batch_no_group_id(
826 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
827 GAIA_PROF_SCOPE(query::run_query_batch_no_group_id);
831 ChunkBatchArray chunkBatches;
833 auto cacheView = queryInfo.cache_archetype_view();
834 auto sortView = queryInfo.cache_sort_view();
836 lock(*m_storage.world());
838 if (!sortView.empty()) {
839 for (
const auto& view: sortView) {
840 const auto chunkEntitiesCnt = TIter::size(view.pChunk);
841 if GAIA_UNLIKELY (chunkEntitiesCnt == 0)
844 const auto viewFrom = view.startRow;
845 const auto viewTo = (uint16_t)(view.startRow + view.
count);
847 const auto minStartRow = TIter::start_index(view.pChunk);
848 const auto minEndRow = TIter::end_index(view.pChunk);
849 const auto startRow = core::get_max(minStartRow, viewFrom);
850 const auto endRow = core::get_min(minEndRow, viewTo);
851 const auto totalRows = endRow - startRow;
855 if constexpr (HasFilters) {
856 if (!match_filters(*view.pChunk, queryInfo))
860 auto* pArchetype = cacheView[view.archetypeIdx];
863 chunkBatches.push_back(ChunkBatch{pArchetype, view.pChunk, indicesView.data(), 0U, startRow, endRow});
865 if GAIA_UNLIKELY (chunkBatches.size() == chunkBatches.max_size()) {
866 run_query_func<Func, TIter>(m_storage.world(), func, {chunkBatches.data(), chunkBatches.size()});
867 chunkBatches.clear();
871 for (uint32_t i = idxFrom; i < idxTo; ++i) {
872 auto* pArchetype = cacheView[i];
873 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
877 const auto& chunks = pArchetype->chunks();
879 uint32_t chunkOffset = 0;
880 uint32_t itemsLeft = chunks.size();
881 while (itemsLeft > 0) {
882 const auto maxBatchSize = chunkBatches.max_size() - chunkBatches.size();
883 const auto batchSize = itemsLeft > maxBatchSize ? maxBatchSize : itemsLeft;
885 ChunkSpanMut chunkSpan((Chunk**)&chunks[chunkOffset], batchSize);
886 for (
auto* pChunk: chunkSpan) {
887 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
890 if constexpr (HasFilters) {
891 if (!match_filters(*pChunk, queryInfo))
895 chunkBatches.push_back({pArchetype, pChunk, indicesView.data(), 0, 0, 0});
898 if GAIA_UNLIKELY (chunkBatches.size() == chunkBatches.max_size()) {
899 run_query_func<Func, TIter>(m_storage.world(), func, {chunkBatches.data(), chunkBatches.size()});
900 chunkBatches.clear();
903 itemsLeft -= batchSize;
904 chunkOffset += batchSize;
910 if (!chunkBatches.empty())
911 run_query_func<Func, TIter>(m_storage.world(), func, {chunkBatches.data(), chunkBatches.size()});
913 unlock(*m_storage.world());
916 commit_cmd_buffer_st(*m_storage.world());
917 commit_cmd_buffer_mt(*m_storage.world());
920 template <
bool HasFilters,
typename TIter,
typename Func, QueryExecType ExecType>
921 void run_query_batch_no_group_id_par(
922 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
923 static_assert(ExecType != QueryExecType::Default);
924 GAIA_PROF_SCOPE(query::run_query_batch_no_group_id_par);
926 auto cacheView = queryInfo.cache_archetype_view();
927 auto sortView = queryInfo.cache_sort_view();
929 if (!sortView.empty()) {
930 for (
const auto& view: sortView) {
931 const auto chunkEntitiesCnt = TIter::size(view.pChunk);
932 if GAIA_UNLIKELY (chunkEntitiesCnt == 0)
935 const auto viewFrom = view.startRow;
936 const auto viewTo = (uint16_t)(view.startRow + view.
count);
938 const auto minStartRow = TIter::start_index(view.pChunk);
939 const auto minEndRow = TIter::end_index(view.pChunk);
940 const auto startRow = core::get_max(minStartRow, viewFrom);
941 const auto endRow = core::get_min(minEndRow, viewTo);
942 const auto totalRows = endRow - startRow;
946 if constexpr (HasFilters) {
947 if (!match_filters(*view.pChunk, queryInfo))
951 const auto* pArchetype = cacheView[view.archetypeIdx];
952 auto indicesView = queryInfo.indices_mapping_view(view.archetypeIdx);
954 m_batches.push_back(ChunkBatch{pArchetype, view.pChunk, indicesView.data(), 0U, startRow, endRow});
957 for (uint32_t i = idxFrom; i < idxTo; ++i) {
958 const auto* pArchetype = cacheView[i];
959 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
962 auto indicesView = queryInfo.indices_mapping_view(i);
963 const auto& chunks = pArchetype->chunks();
964 for (
auto* pChunk: chunks) {
965 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
968 if constexpr (HasFilters) {
969 if (!match_filters(*pChunk, queryInfo))
973 m_batches.push_back({pArchetype, pChunk, indicesView.data(), 0, 0, 0});
978 if (m_batches.empty())
981 lock(*m_storage.world());
986 if constexpr (ExecType == QueryExecType::ParallelEff)
987 j.priority = mt::JobPriority::Low;
989 j.func = [&](
const mt::JobArgs& args) {
990 run_query_func<Func, TIter>(
991 m_storage.world(), func,
std::span(&m_batches[args.idxStart], args.idxEnd - args.idxStart));
994 auto& tp = mt::ThreadPool::get();
995 auto jobHandle = tp.sched_par(j, m_batches.size(), 0);
999 unlock(*m_storage.world());
1002 commit_cmd_buffer_st(*m_storage.world());
1003 commit_cmd_buffer_mt(*m_storage.world());
1006 template <
bool HasFilters,
typename TIter,
typename Func>
1007 void run_query_batch_with_group_id(
1008 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
1009 GAIA_PROF_SCOPE(query::run_query_batch_with_group_id);
1011 ChunkBatchArray chunkBatches;
1013 auto cacheView = queryInfo.cache_archetype_view();
1014 auto dataView = queryInfo.cache_data_view();
1016 lock(*m_storage.world());
1018 for (uint32_t i = idxFrom; i < idxTo; ++i) {
1019 const auto* pArchetype = cacheView[i];
1020 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
1023 auto indicesView = queryInfo.indices_mapping_view(i);
1024 const auto& chunks = pArchetype->chunks();
1025 const auto& data = dataView[i];
1027#if GAIA_ASSERT_ENABLED
1030 queryInfo.ctx().data.groupIdSet == 0 ||
1032 data.groupId == queryInfo.ctx().data.groupIdSet);
1035 uint32_t chunkOffset = 0;
1036 uint32_t itemsLeft = chunks.size();
1037 while (itemsLeft > 0) {
1038 const auto maxBatchSize = chunkBatches.max_size() - chunkBatches.size();
1039 const auto batchSize = itemsLeft > maxBatchSize ? maxBatchSize : itemsLeft;
1041 ChunkSpanMut chunkSpan((Chunk**)&chunks[chunkOffset], batchSize);
1042 for (
auto* pChunk: chunkSpan) {
1043 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
1046 if constexpr (HasFilters) {
1047 if (!match_filters(*pChunk, queryInfo))
1051 chunkBatches.push_back({pArchetype, pChunk, indicesView.data(), data.groupId, 0, 0});
1054 if GAIA_UNLIKELY (chunkBatches.size() == chunkBatches.max_size()) {
1055 run_query_func<Func, TIter>(m_storage.world(), func, {chunkBatches.data(), chunkBatches.size()});
1056 chunkBatches.clear();
1059 itemsLeft -= batchSize;
1060 chunkOffset += batchSize;
1065 if (!chunkBatches.empty())
1066 run_query_func<Func, TIter>(m_storage.world(), func, {chunkBatches.data(), chunkBatches.size()});
1068 unlock(*m_storage.world());
1071 commit_cmd_buffer_st(*m_storage.world());
1072 commit_cmd_buffer_mt(*m_storage.world());
1075 template <
bool HasFilters,
typename TIter,
typename Func, QueryExecType ExecType>
1076 void run_query_batch_with_group_id_par(
1077 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
1078 static_assert(ExecType != QueryExecType::Default);
1079 GAIA_PROF_SCOPE(query::run_query_batch_with_group_id_par);
1081 ChunkBatchArray chunkBatch;
1083 auto cacheView = queryInfo.cache_archetype_view();
1084 auto dataView = queryInfo.cache_data_view();
1086#if GAIA_ASSERT_ENABLED
1087 for (uint32_t i = idxFrom; i < idxTo; ++i) {
1088 auto* pArchetype = cacheView[i];
1089 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
1092 const auto& data = dataView[i];
1095 queryInfo.ctx().data.groupIdSet == 0 ||
1097 data.groupId == queryInfo.ctx().data.groupIdSet);
1101 for (uint32_t i = idxFrom; i < idxTo; ++i) {
1102 const Archetype* pArchetype = cacheView[i];
1103 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
1106 auto indicesView = queryInfo.indices_mapping_view(i);
1107 const auto& data = dataView[i];
1108 const auto& chunks = pArchetype->chunks();
1109 for (
auto* pChunk: chunks) {
1110 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
1113 if constexpr (HasFilters) {
1114 if (!match_filters(*pChunk, queryInfo))
1118 m_batches.push_back({pArchetype, pChunk, indicesView.data(), data.groupId, 0, 0});
1122 if (m_batches.empty())
1125 lock(*m_storage.world());
1130 if constexpr (ExecType == QueryExecType::ParallelEff)
1131 j.priority = mt::JobPriority::Low;
1133 j.func = [&](
const mt::JobArgs& args) {
1134 run_query_func<Func, TIter>(
1135 m_storage.world(), func,
std::span(&m_batches[args.idxStart], args.idxEnd - args.idxStart));
1138 auto& tp = mt::ThreadPool::get();
1139 auto jobHandle = tp.sched_par(j, m_batches.size(), 0);
1143 unlock(*m_storage.world());
1146 commit_cmd_buffer_st(*m_storage.world());
1147 commit_cmd_buffer_mt(*m_storage.world());
1150 template <
bool HasFilters, QueryExecType ExecType,
typename TIter,
typename Func>
1151 void run_query(
const QueryInfo& queryInfo, Func func) {
1152 GAIA_PROF_SCOPE(query::run_query);
1159 auto cache_view = queryInfo.cache_archetype_view();
1160 if (cache_view.empty())
1163 const bool isGroupBy = queryInfo.ctx().data.groupBy != EntityBad;
1164 const bool isGroupSet = queryInfo.ctx().data.groupIdSet != 0;
1165 if (!isGroupBy || !isGroupSet) {
1167 const auto idxFrom = 0;
1168 const auto idxTo = (uint32_t)cache_view.size();
1169 if constexpr (ExecType != QueryExecType::Default)
1170 run_query_batch_no_group_id_par<HasFilters, TIter, Func, ExecType>(queryInfo, idxFrom, idxTo, func);
1172 run_query_batch_no_group_id<HasFilters, TIter, Func>(queryInfo, idxFrom, idxTo, func);
1177 auto group_data_view = queryInfo.group_data_view();
1178 const auto cnt = group_data_view.size();
1180 if (group_data_view[i].groupId != queryInfo.ctx().data.groupIdSet)
1183 const auto idxFrom = group_data_view[i].idxFirst;
1184 const auto idxTo = group_data_view[i].idxLast + 1;
1185 if constexpr (ExecType != QueryExecType::Default)
1186 run_query_batch_with_group_id_par<HasFilters, TIter, Func, ExecType>(queryInfo, idxFrom, idxTo, func);
1188 run_query_batch_with_group_id<HasFilters, TIter, Func>(queryInfo, idxFrom, idxTo, func);
1194 template <QueryExecType ExecType,
typename TIter,
typename Func>
1195 void run_query_on_chunks(QueryInfo& queryInfo, Func func) {
1197 ::gaia::ecs::update_version(*m_worldVersion);
1199 const bool hasFilters = queryInfo.has_filters();
1201 run_query<true, ExecType, TIter>(queryInfo, func);
1203 run_query<false, ExecType, TIter>(queryInfo, func);
1206 queryInfo.set_world_version(*m_worldVersion);
1209 template <
typename TIter,
typename Func,
typename... T>
1210 GAIA_FORCEINLINE
void
1211 run_query_on_chunk(TIter& it, Func func, [[maybe_unused]] core::func_type_list<T...> types) {
1212 const auto cnt = it.size();
1214 if constexpr (
sizeof...(T) > 0) {
1220 auto dataPointerTuple = std::make_tuple(it.template view_auto<T>()...);
1227 func(std::get<
decltype(it.template view_auto<T>())>(dataPointerTuple)[it.template acc_index<T>(i)]...);
1231 GAIA_FOR(cnt) func();
1235 template <QueryExecType ExecType,
typename Func>
1236 void each_inter(QueryInfo& queryInfo, Func func) {
1237 using InputArgs =
decltype(core::func_args(&Func::operator()));
1239#if GAIA_ASSERT_ENABLED
1254 GAIA_ASSERT(unpack_args_into_query_has_all(queryInfo, InputArgs{}));
1257 run_query_on_chunks<ExecType, Iter>(queryInfo, [&](Iter& it) {
1258 GAIA_PROF_SCOPE(query_func);
1259 run_query_on_chunk(it, func, InputArgs{});
1264 QueryExecType ExecType,
typename Func,
bool FuncEnabled = UseCaching,
1265 typename std::enable_if<FuncEnabled>::type* =
nullptr>
1266 void each_inter(QueryId queryId, Func func) {
1269 GAIA_ASSERT(queryId != QueryIdBad);
1272 each_inter(queryInfo, func);
1275 template <QueryExecType ExecType,
typename Func>
1276 void each_inter(Func func) {
1277 auto& queryInfo =
fetch();
1279 if constexpr (std::is_invocable_v<Func, IterAll&>) {
1280 run_query_on_chunks<ExecType, IterAll>(queryInfo, [&](IterAll& it) {
1281 GAIA_PROF_SCOPE(query_func);
1284 }
else if constexpr (std::is_invocable_v<Func, Iter&>) {
1285 run_query_on_chunks<ExecType, Iter>(queryInfo, [&](Iter& it) {
1286 GAIA_PROF_SCOPE(query_func);
1289 }
else if constexpr (std::is_invocable_v<Func, IterDisabled&>) {
1290 run_query_on_chunks<ExecType, IterDisabled>(queryInfo, [&](IterDisabled& it) {
1291 GAIA_PROF_SCOPE(query_func);
1295 each_inter<ExecType>(queryInfo, func);
1298 template <
bool UseFilters,
typename TIter>
1299 GAIA_NODISCARD
bool empty_inter(
const QueryInfo& queryInfo)
const {
1300 for (
auto* pArchetype: queryInfo) {
1301 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
1304 GAIA_PROF_SCOPE(query::empty);
1306 const auto& chunks = pArchetype->chunks();
1308 it.set_world(queryInfo.world());
1309 it.set_archetype(pArchetype);
1311 const bool isNotEmpty = core::has_if(chunks, [&](Chunk* pChunk) {
1312 it.set_chunk(pChunk);
1313 if constexpr (UseFilters)
1314 return it.size() > 0 && match_filters(*pChunk, queryInfo);
1316 return it.size() > 0;
1326 template <
bool UseFilters,
typename TIter>
1327 GAIA_NODISCARD uint32_t count_inter(
const QueryInfo& queryInfo)
const {
1330 it.set_world(queryInfo.world());
1332 for (
auto* pArchetype: queryInfo) {
1333 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
1336 GAIA_PROF_SCOPE(query::count);
1338 it.set_archetype(pArchetype);
1343 const auto& chunks = pArchetype->chunks();
1344 for (
auto* pChunk: chunks) {
1345 it.set_chunk(pChunk);
1347 const auto entityCnt = it.size();
1352 if constexpr (UseFilters) {
1353 if (!match_filters(*pChunk, queryInfo))
1365 template <
bool UseFilters,
typename TIter,
typename ContainerOut>
1366 void arr_inter(QueryInfo& queryInfo, ContainerOut& outArray) {
1367 using ContainerItemType =
typename ContainerOut::value_type;
1369 it.set_world(queryInfo.world());
1371 for (
auto* pArchetype: queryInfo) {
1372 if GAIA_UNLIKELY (!can_process_archetype(*pArchetype))
1375 GAIA_PROF_SCOPE(query::arr);
1377 it.set_archetype(pArchetype);
1382 const auto& chunks = pArchetype->chunks();
1383 for (
auto* pChunk: chunks) {
1384 it.set_chunk(pChunk);
1386 const auto cnt = it.size();
1391 if constexpr (UseFilters) {
1392 if (!match_filters(*pChunk, queryInfo))
1396 const auto dataView = it.template view<ContainerItemType>();
1398 const auto idx = it.template acc_index<ContainerItemType>(i);
1399 auto tmp = dataView[idx];
1400 outArray.push_back(tmp);
1407 QueryImpl() =
default;
1408 ~QueryImpl() =
default;
1410 template <
bool FuncEnabled = UseCaching>
1412 World& world, QueryCache& queryCache, ArchetypeId& nextArchetypeId, uint32_t& worldVersion,
1413 const ArchetypeMapById& archetypes,
const EntityToArchetypeMap& entityToArchetypeMap,
1414 const ArchetypeDArray& allArchetypes):
1415 m_nextArchetypeId(&nextArchetypeId), m_worldVersion(&worldVersion), m_archetypes(&archetypes),
1416 m_entityToArchetypeMap(&entityToArchetypeMap), m_allArchetypes(&allArchetypes) {
1417 m_storage.init(&world, &queryCache);
1420 template <
bool FuncEnabled = !UseCaching>
1422 World& world, ArchetypeId& nextArchetypeId, uint32_t& worldVersion,
const ArchetypeMapById& archetypes,
1423 const EntityToArchetypeMap& entityToArchetypeMap,
const ArchetypeDArray& allArchetypes):
1424 m_nextArchetypeId(&nextArchetypeId), m_worldVersion(&worldVersion), m_archetypes(&archetypes),
1425 m_entityToArchetypeMap(&entityToArchetypeMap), m_allArchetypes(&allArchetypes) {
1426 m_storage.init(&world);
1429 GAIA_NODISCARD QueryId id()
const {
1430 static_assert(UseCaching,
"id() can be used only with cached queries");
1434 GAIA_NODISCARD uint32_t gen()
const {
1435 static_assert(UseCaching,
"gen() can be used only with cached queries");
1487 GAIA_ASSERT(str !=
nullptr);
1492 va_start(args, str);
1497 auto process = [&]() {
1501 auto expr = core::trim(exprRaw);
1505 if (expr[0] ==
'+') {
1510 auto var = expr.subspan(off);
1511 auto entity = expr_to_entity((
const World&)*m_storage.world(), args, var);
1512 if (entity == EntityBad)
1515 any(entity, off != 0);
1516 }
else if (expr[0] ==
'!') {
1517 auto var = expr.subspan(1);
1518 auto entity = expr_to_entity((
const World&)*m_storage.world(), args, var);
1519 if (entity == EntityBad)
1524 auto entity = expr_to_entity((
const World&)*m_storage.world(), args, expr);
1525 if (entity == EntityBad)
1534 for (; str[pos] != 0; ++pos) {
1535 if (str[pos] ==
';' && !process())
1553 QueryImpl& all(
Entity entity,
bool isReadWrite =
false) {
1555 add({QueryOpKind::All, QueryAccess::None, entity});
1557 add({QueryOpKind::All, isReadWrite ? QueryAccess::Write : QueryAccess::Read, entity});
1561 QueryImpl& all(Entity entity, Entity src,
bool isReadWrite =
false) {
1563 add({QueryOpKind::All, QueryAccess::None, entity, src});
1565 add({QueryOpKind::All, isReadWrite ? QueryAccess::Write : QueryAccess::Read, entity, src});
1569#if GAIA_USE_VARIADIC_API
1570 template <
typename... T>
1573 (add_inter<T>(QueryOpKind::All), ...);
1577 template <
typename T>
1580 add_inter<T>(QueryOpKind::All);
1587 QueryImpl& any(Entity entity,
bool isReadWrite =
false) {
1589 add({QueryOpKind::Any, QueryAccess::None, entity});
1591 add({QueryOpKind::Any, isReadWrite ? QueryAccess::Write : QueryAccess::Read, entity});
1595#if GAIA_USE_VARIADIC_API
1596 template <
typename... T>
1599 (add_inter<T>(QueryOpKind::Any), ...);
1603 template <
typename T>
1606 add_inter<T>(QueryOpKind::Any);
1613 QueryImpl& no(Entity entity) {
1614 add({QueryOpKind::Not, QueryAccess::None, entity});
1618#if GAIA_USE_VARIADIC_API
1619 template <
typename... T>
1622 (add_inter<T>(QueryOpKind::Not), ...);
1626 template <
typename T>
1629 add_inter<T>(QueryOpKind::Not);
1636 QueryImpl& changed(Entity entity) {
1637 changed_inter(entity);
1641#if GAIA_USE_VARIADIC_API
1642 template <
typename... T>
1643 QueryImpl& changed() {
1645 (changed_inter<T>(), ...);
1649 template <
typename T>
1650 QueryImpl& changed() {
1665 sort_by_inter(entity, func);
1673 template <
typename T>
1675 sort_by_inter<T>(func);
1684 template <
typename Rel,
typename Tgt>
1686 sort_by_inter<Rel, Tgt>(func);
1696 group_by_inter(entity, func);
1703 template <
typename T>
1705 group_by_inter<T>(func);
1713 template <
typename Rel,
typename Tgt>
1715 group_by_inter<Rel, Tgt>(func);
1724 set_group_id_inter(groupId);
1731 GAIA_ASSERT(!entity.pair());
1732 set_group_id_inter(entity.id());
1738 template <
typename T>
1740 set_group_id_inter<T>();
1746 template <
typename Func>
1747 void each(Func func) {
1748 each_inter<QueryExecType::Default, Func>(func);
1751 template <
typename Func>
1752 void each(Func func, QueryExecType execType) {
1754 case QueryExecType::Parallel:
1755 each_inter<QueryExecType::Parallel, Func>(func);
1757 case QueryExecType::ParallelPerf:
1758 each_inter<QueryExecType::ParallelPerf, Func>(func);
1760 case QueryExecType::ParallelEff:
1761 each_inter<QueryExecType::ParallelEff, Func>(func);
1764 each_inter<QueryExecType::Default, Func>(func);
1777 bool empty(Constraints constraints = Constraints::EnabledOnly) {
1778 auto& queryInfo =
fetch();
1779 const bool hasFilters = queryInfo.has_filters();
1782 switch (constraints) {
1783 case Constraints::EnabledOnly:
1784 return empty_inter<true, Iter>(queryInfo);
1785 case Constraints::DisabledOnly:
1786 return empty_inter<true, IterDisabled>(queryInfo);
1787 case Constraints::AcceptAll:
1788 return empty_inter<true, IterAll>(queryInfo);
1791 switch (constraints) {
1792 case Constraints::EnabledOnly:
1793 return empty_inter<false, Iter>(queryInfo);
1794 case Constraints::DisabledOnly:
1795 return empty_inter<false, IterDisabled>(queryInfo);
1796 case Constraints::AcceptAll:
1797 return empty_inter<false, IterAll>(queryInfo);
1809 uint32_t
count(Constraints constraints = Constraints::EnabledOnly) {
1810 auto& queryInfo =
fetch();
1811 uint32_t entCnt = 0;
1813 const bool hasFilters = queryInfo.has_filters();
1815 switch (constraints) {
1816 case Constraints::EnabledOnly: {
1817 entCnt += count_inter<true, Iter>(queryInfo);
1819 case Constraints::DisabledOnly: {
1820 entCnt += count_inter<true, IterDisabled>(queryInfo);
1822 case Constraints::AcceptAll: {
1823 entCnt += count_inter<true, IterAll>(queryInfo);
1827 switch (constraints) {
1828 case Constraints::EnabledOnly: {
1829 entCnt += count_inter<false, Iter>(queryInfo);
1831 case Constraints::DisabledOnly: {
1832 entCnt += count_inter<false, IterDisabled>(queryInfo);
1834 case Constraints::AcceptAll: {
1835 entCnt += count_inter<false, IterAll>(queryInfo);
1847 template <
typename Container>
1848 void arr(Container& outArray, Constraints constraints = Constraints::EnabledOnly) {
1849 const auto entCnt =
count();
1853 outArray.reserve(entCnt);
1854 auto& queryInfo =
fetch();
1856 const bool hasFilters = queryInfo.has_filters();
1858 switch (constraints) {
1859 case Constraints::EnabledOnly:
1860 arr_inter<true, Iter>(queryInfo, outArray);
1862 case Constraints::DisabledOnly:
1863 arr_inter<true, IterDisabled>(queryInfo, outArray);
1865 case Constraints::AcceptAll:
1866 arr_inter<true, IterAll>(queryInfo, outArray);
1870 switch (constraints) {
1871 case Constraints::EnabledOnly:
1872 arr_inter<false, Iter>(queryInfo, outArray);
1874 case Constraints::DisabledOnly:
1875 arr_inter<false, IterDisabled>(queryInfo, outArray);
1877 case Constraints::AcceptAll:
1878 arr_inter<false, IterAll>(queryInfo, outArray);
1887 auto& info =
fetch();
1888 GAIA_LOG_N(
"DIAG Query %u.%u [%c]",
id(), gen(), UseCaching ?
'C' :
'U');
1889 for (
const auto* pArchetype: info)
1890 Archetype::diag_basic_info(*m_storage.world(), *pArchetype);
1891 GAIA_LOG_N(
"END DIAG Query");