460 static constexpr uint32_t ChunkBatchSize = 32;
465 const uint8_t* pCompIndices;
466 InheritedTermDataView inheritedData;
477 struct DirectQueryScratch {
483 uint32_t seenVersion = 1;
487 GAIA_NODISCARD
bool uses_query_cache_storage()
const {
488 return m_cacheKind != QueryCacheKind::None;
491 GAIA_NODISCARD
bool uses_shared_cache_layer()
const {
492 return uses_query_cache_storage() && m_cacheScope == QueryCacheScope::Shared;
495 void invalidate_query_storage() {
496 if (uses_query_cache_storage())
503 GAIA_NODISCARD
static DirectQueryScratch& direct_query_scratch() {
504 static thread_local DirectQueryScratch scratch;
511 static void ensure_direct_query_count_capacity(DirectQueryScratch& scratch, uint32_t entityId) {
512 if (entityId < scratch.counts.size())
515 const auto doubledSize = (uint32_t)scratch.counts.size() * 2U;
516 const auto minSize = doubledSize > 64U ? doubledSize : 64U;
517 const auto newSize = (entityId + 1U) > minSize ? (entityId + 1U) : minSize;
518 scratch.counts.resize(newSize, 0);
524 GAIA_NODISCARD
static uint32_t next_direct_query_seen_version(DirectQueryScratch& scratch) {
525 update_version(scratch.seenVersion);
526 if (scratch.seenVersion == 0) {
527 scratch.seenVersion = 1;
528 core::fill(scratch.counts.begin(), scratch.counts.end(), 0);
531 return scratch.seenVersion;
534 static constexpr CmdFunc CommandBufferRead[] = {
538 ser::load(buffer, cmd);
544 ser::load(buffer, cmd);
550 ser::load(buffer, cmd);
556 ser::load(buffer, cmd);
562 ser::load(buffer, cmd);
568 ser::load(buffer, cmd);
576 ArchetypeId* m_nextArchetypeId{};
578 uint32_t* m_worldVersion{};
586 uint8_t m_varNamesMask = 0;
590 uint8_t m_varBindingsMask = 0;
592 GroupId m_groupIdSet = 0;
594 uint32_t m_changedWorldVersion = 0;
599 QueryCacheKind m_cacheKind = QueryCacheKind::Default;
601 QueryCacheScope m_cacheScope = QueryCacheScope::Local;
603 uint16_t m_cacheSrcTrav = 0;
606 struct EachWalkData {
614 Entity cachedRelation = EntityBad;
616 Constraints cachedConstraints = Constraints::EnabledOnly;
618 uint32_t cachedRelationVersion = 0;
620 uint32_t cachedEntityVersion = 0;
624 bool cacheValid =
false;
645 template <
typename T>
646 struct OnDemandDataHolder {
649 OnDemandDataHolder() =
default;
651 ~OnDemandDataHolder() {
655 OnDemandDataHolder(
const OnDemandDataHolder& other) {
656 if (other.pData !=
nullptr)
657 pData =
new T(*other.pData);
660 OnDemandDataHolder& operator=(
const OnDemandDataHolder& other) {
661 if (core::addressof(other) ==
this)
664 if (other.pData ==
nullptr) {
670 if (pData ==
nullptr)
671 pData =
new T(*other.pData);
673 *pData = *other.pData;
678 OnDemandDataHolder(OnDemandDataHolder&& other)
noexcept: pData(other.pData) {
679 other.pData =
nullptr;
682 OnDemandDataHolder& operator=(OnDemandDataHolder&& other)
noexcept {
683 if (core::addressof(other) ==
this)
688 other.pData =
nullptr;
692 GAIA_NODISCARD T* get() {
696 GAIA_NODISCARD
const T* get()
const {
700 GAIA_NODISCARD T& ensure() {
701 if (pData ==
nullptr)
713 OnDemandDataHolder<EachWalkData> m_eachWalkData;
716 struct DirectSeedRunData {
720 Entity cachedSeedTerm = EntityBad;
721 QueryMatchKind cachedSeedMatchKind = QueryMatchKind::Semantic;
722 Constraints cachedConstraints = Constraints::EnabledOnly;
723 uint32_t cachedRelVersion = 0;
724 uint32_t cachedWorldVersion = 0;
725 bool cacheValid =
false;
728 OnDemandDataHolder<DirectSeedRunData> m_directSeedRunData;
732 static inline bool SilenceInvalidCacheKindAssertions =
false;
738 GAIA_PROF_SCOPE(query::fetch);
743 if (!uses_query_cache_storage()) {
746 ctx.init(m_storage.
world());
749 QueryInfo::create(QueryId{}, GAIA_MOV(ctx), *m_entityToArchetypeMap, all_archetypes_view()));
761 if GAIA_UNLIKELY (pQueryInfo ==
nullptr)
765 if GAIA_LIKELY (pQueryInfo !=
nullptr) {
768 recommit(pQueryInfo->ctx());
777 ctx.init(m_storage.
world());
780 uses_shared_cache_layer()
781 ? m_storage.
m_pCache->
add(GAIA_MOV(ctx), *m_entityToArchetypeMap, all_archetypes_view())
782 : m_storage.
m_pCache->
add_local(GAIA_MOV(ctx), *m_entityToArchetypeMap, all_archetypes_view());
792 const auto kindError = validate_kind(queryInfo.ctx());
793 if (kindError != QueryKindRes::OK) {
794 GAIA_ASSERT2(SilenceInvalidCacheKindAssertions,
kind_error_str(kindError));
799 if (!uses_query_cache_storage()) {
800 queryInfo.ensure_matches_transient(
801 *m_entityToArchetypeMap, all_archetypes_view(), m_varBindings, m_varBindingsMask);
805 queryInfo.ensure_matches(
806 *m_entityToArchetypeMap, all_archetypes_view(), last_archetype_id(), m_varBindings, m_varBindingsMask);
807 m_storage.
m_pCache->sync_archetype_cache(queryInfo);
816 if (!uses_query_cache_storage()) {
817 return queryInfo.ensure_matches_one_transient(archetype, targetEntities, m_varBindings, m_varBindingsMask);
820 return queryInfo.ensure_matches_one(archetype, targetEntities, m_varBindings, m_varBindingsMask);
829 const auto kindError = validate_kind(queryInfo.ctx());
830 if (kindError != QueryKindRes::OK) {
831 GAIA_ASSERT2(SilenceInvalidCacheKindAssertions,
kind_error_str(kindError));
844 return fetch().cache_policy();
854 if (m_cacheSrcTrav == maxItems)
857 if (maxItems > MaxCacheSrcTrav) {
858 GAIA_ASSERT(
false &&
"cache_src_trav should be a value smaller than MaxCacheSrcTrav");
859 maxItems = MaxCacheSrcTrav;
862 invalidate_each_walk_cache();
863 invalidate_direct_seed_run_cache();
864 invalidate_query_storage();
865 m_cacheSrcTrav = maxItems;
873 return m_cacheSrcTrav;
880 if (m_cacheKind == cacheKind)
883 invalidate_each_walk_cache();
884 invalidate_direct_seed_run_cache();
885 invalidate_query_storage();
886 m_cacheKind = cacheKind;
895 if (m_cacheScope == cacheScope)
898 invalidate_each_walk_cache();
899 invalidate_direct_seed_run_cache();
900 invalidate_query_storage();
901 m_cacheScope = cacheScope;
916 GAIA_NODISCARD QueryCacheScope
scope()
const {
922 GAIA_NODISCARD QueryCacheKind
kind()
const {
929 return validate_kind(
fetch().ctx());
949 GAIA_NODISCARD
bool uses_manual_src_trav_cache(
const QueryCtx& ctx)
const {
950 return m_cacheSrcTrav != 0 &&
951 ctx.data.
deps.has_dep_flag(QueryCtx::DependencyHasSourceTerms) &&
952 ctx.data.
deps.has_dep_flag(QueryCtx::DependencyHasTraversalTerms);
958 GAIA_NODISCARD
static bool uses_im_cache(
const QueryCtx& ctx) {
959 return ctx.data.
cachePolicy == QueryCachePolicy::Immediate;
965 GAIA_NODISCARD
static bool uses_lazy_cache(
const QueryCtx& ctx) {
966 return ctx.data.cachePolicy == QueryCachePolicy::Lazy;
972 GAIA_NODISCARD
static bool uses_dyn_cache(
const QueryCtx& ctx) {
973 return ctx.data.cachePolicy == QueryCachePolicy::Dynamic;
979 GAIA_NODISCARD QueryKindRes validate_kind(
const QueryCtx& ctx)
const {
980 if (m_cacheKind == QueryCacheKind::Auto) {
981 if (uses_manual_src_trav_cache(ctx))
982 return QueryKindRes::AutoSrcTrav;
985 if (m_cacheKind == QueryCacheKind::All) {
986 if (uses_manual_src_trav_cache(ctx))
987 return QueryKindRes::AllSrcTrav;
988 if (!uses_im_cache(ctx))
989 return QueryKindRes::AllNotIm;
992 return QueryKindRes::OK;
998 GAIA_NODISCARD
static const char*
kind_error_str(QueryKindRes error) {
1000 case QueryKindRes::OK:
1002 case QueryKindRes::AutoSrcTrav:
1003 return "QueryCacheKind::Auto rejects explicit traversed-source snapshot caching";
1004 case QueryKindRes::AllNotIm:
1005 return "QueryCacheKind::All requires a fully immediate structural cache";
1006 case QueryKindRes::AllSrcTrav:
1007 return "QueryCacheKind::All rejects explicit traversed-source snapshot caching";
1009 return "Unknown query kind validation error";
1015 GAIA_NODISCARD EachWalkData* each_walk_data() {
1016 return m_eachWalkData.get();
1021 GAIA_NODISCARD
const EachWalkData* each_walk_data()
const {
1022 return m_eachWalkData.get();
1027 GAIA_NODISCARD EachWalkData& ensure_each_walk_data() {
1028 return m_eachWalkData.ensure();
1032 void invalidate_each_walk_cache() {
1033 auto* pWalkData = each_walk_data();
1034 if (pWalkData !=
nullptr)
1035 pWalkData->cacheValid =
false;
1040 GAIA_NODISCARD DirectSeedRunData* direct_seed_run_data() {
1041 return m_directSeedRunData.get();
1046 GAIA_NODISCARD
const DirectSeedRunData* direct_seed_run_data()
const {
1047 return m_directSeedRunData.get();
1052 GAIA_NODISCARD DirectSeedRunData& ensure_direct_seed_run_data() {
1053 return m_directSeedRunData.ensure();
1057 void invalidate_direct_seed_run_cache() {
1058 auto* pRunData = direct_seed_run_data();
1059 if (pRunData !=
nullptr)
1060 pRunData->cacheValid =
false;
1064 void reset_changed_filter_state() {
1065 m_changedWorldVersion = 0;
1070 ArchetypeId last_archetype_id()
const {
1071 return *m_nextArchetypeId - 1;
1077 GAIA_ASSERT(m_allArchetypes !=
nullptr);
1078 return {(
const Archetype**)m_allArchetypes->data(), m_allArchetypes->size()};
1081 GAIA_NODISCARD
static bool is_query_var_entity(Entity entity) {
1082 return is_variable((EntityId)entity.id());
1085 GAIA_NODISCARD
static uint32_t query_var_idx(Entity entity) {
1086 GAIA_ASSERT(is_query_var_entity(entity));
1087 return (uint32_t)(entity.id() - Var0.id());
1090 GAIA_NODISCARD Entity query_var_entity(uint32_t idx) {
1091 GAIA_ASSERT(idx < 8);
1092 return entity_from_id((
const World&)*m_storage.
world(), (EntityId)(Var0.id() + idx));
1095 GAIA_NODISCARD
static util::str_view normalize_var_name(util::str_view name) {
1096 auto trimmed = util::trim(name);
1097 if (trimmed.empty())
1100 if (trimmed.data()[0] ==
'$') {
1101 if (trimmed.size() == 1)
1103 trimmed = util::str_view(trimmed.data() + 1, trimmed.size() - 1);
1106 return util::trim(trimmed);
1109 GAIA_NODISCARD
static bool is_reserved_var_name(util::str_view varName) {
1110 return varName ==
"this";
1113 GAIA_NODISCARD Entity find_var_by_name(util::str_view rawName) {
1114 const auto varName = normalize_var_name(rawName);
1115 if (varName.empty() || is_reserved_var_name(varName))
1119 const auto bit = (uint8_t(1) << i);
1120 if ((m_varNamesMask & bit) == 0)
1122 if (m_varNames[i] == varName)
1123 return query_var_entity(i);
1129 bool set_var_name_internal(Entity varEntity, util::str_view rawName) {
1130 if (!is_query_var_entity(varEntity))
1133 const auto varName = normalize_var_name(rawName);
1134 if (varName.empty() || is_reserved_var_name(varName))
1137 const auto idx = query_var_idx(varEntity);
1138 const auto bit = (uint8_t(1) << idx);
1144 const auto otherBit = (uint8_t(1) << i);
1145 if ((m_varNamesMask & otherBit) == 0)
1147 if (!(m_varNames[i] == varName))
1150 GAIA_ASSERT2(
false,
"Variable name is already assigned to a different query variable");
1154 m_varNames[idx].assign(varName);
1155 m_varNamesMask |= bit;
1159 template <
typename T>
1160 void add_cmd(T& cmd) {
1161 invalidate_each_walk_cache();
1164 if constexpr (T::InvalidatesHash) {
1165 reset_changed_filter_state();
1170 ser::save(serBuffer, T::Id);
1171 ser::save(serBuffer, T::InvalidatesHash);
1172 ser::save(serBuffer, cmd);
1175 void add_inter(QueryInput item) {
1177 GAIA_ASSERT((item.op != QueryOpKind::Not && item.op != QueryOpKind::Any) || item.access == QueryAccess::None);
1179 QueryCmd_AddItem cmd{item};
1183 GAIA_NODISCARD
static QueryAccess normalize_access(QueryOpKind op, Entity entity, QueryAccess access) {
1184 if (op == QueryOpKind::Not || op == QueryOpKind::Any || entity.pair())
1185 return QueryAccess::None;
1188 if (access == QueryAccess::None)
1189 return QueryAccess::Read;
1194 void add_entity_term(QueryOpKind op, Entity entity,
const QueryTermOptions& options) {
1195 const auto access = normalize_access(op, entity, options.access);
1197 {op, access, entity, options.entSrc, options.entTrav, options.travKind, options.travDepth,
1198 options.matchKind});
1201 template <
typename T>
1202 void add_inter(QueryOpKind op) {
1205 if constexpr (is_pair<T>::value) {
1207 const auto& desc_rel = comp_cache_add<typename T::rel_type>(*m_storage.
world());
1208 const auto& desc_tgt = comp_cache_add<typename T::tgt_type>(*m_storage.
world());
1210 e = Pair(desc_rel.entity, desc_tgt.entity);
1213 const auto& desc = comp_cache_add<T>(*m_storage.
world());
1218 QueryAccess access = QueryAccess::None;
1219 if (op != QueryOpKind::Not && op != QueryOpKind::Any) {
1220 constexpr auto isReadWrite = core::is_mut_v<T>;
1221 access = isReadWrite ? QueryAccess::Write : QueryAccess::Read;
1224 add_inter({op, access, e});
1227 template <
typename T>
1228 void add_inter(QueryOpKind op,
const QueryTermOptions& options) {
1231 if constexpr (is_pair<T>::value) {
1233 const auto& desc_rel = comp_cache_add<typename T::rel_type>(*m_storage.
world());
1234 const auto& desc_tgt = comp_cache_add<typename T::tgt_type>(*m_storage.
world());
1236 e = Pair(desc_rel.entity, desc_tgt.entity);
1239 const auto& desc = comp_cache_add<T>(*m_storage.
world());
1243 QueryAccess access = QueryAccess::None;
1244 if (op != QueryOpKind::Not && op != QueryOpKind::Any) {
1245 if (options.access != QueryAccess::None)
1246 access = options.access;
1248 constexpr auto isReadWrite = core::is_mut_v<T>;
1249 access = isReadWrite ? QueryAccess::Write : QueryAccess::Read;
1254 {op, normalize_access(op, e, access), e, options.entSrc, options.entTrav, options.travKind,
1255 options.travDepth, options.matchKind});
1258 template <
typename Rel,
typename Tgt>
1259 void add_inter(QueryOpKind op) {
1260 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
1261 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
1262 static_assert(core::is_raw_v<UO_Rel>,
"Use add() with raw types only");
1263 static_assert(core::is_raw_v<UO_Tgt>,
"Use add() with raw types only");
1266 const auto& descRel = comp_cache_add<Rel>(*m_storage.
world());
1267 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.
world());
1270 QueryAccess access = QueryAccess::None;
1271 if (op != QueryOpKind::Not && op != QueryOpKind::Any) {
1272 constexpr auto isReadWrite = core::is_mut_v<UO_Rel> || core::is_mut_v<UO_Tgt>;
1273 access = isReadWrite ? QueryAccess::Write : QueryAccess::Read;
1276 add_inter({op, access, {descRel.entity, descTgt.entity}});
1281 void changed_inter(Entity entity) {
1282 QueryCmd_AddFilter cmd{entity};
1286 template <
typename T>
1287 void changed_inter() {
1288 using UO =
typename component_type_t<T>::TypeOriginal;
1289 static_assert(core::is_raw_v<UO>,
"Use changed() with raw types only");
1292 const auto& desc = comp_cache_add<T>(*m_storage.
world());
1293 changed_inter(desc.entity);
1296 template <
typename Rel,
typename Tgt>
1297 void changed_inter() {
1298 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
1299 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
1300 static_assert(core::is_raw_v<UO_Rel>,
"Use changed() with raw types only");
1301 static_assert(core::is_raw_v<UO_Tgt>,
"Use changed() with raw types only");
1304 const auto& descRel = comp_cache_add<Rel>(*m_storage.
world());
1305 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.
world());
1306 changed_inter({descRel.entity, descTgt.entity});
1311 void sort_by_inter(Entity entity, TSortByFunc func) {
1312 QueryCmd_SortBy cmd{entity, func};
1316 template <
typename T>
1317 void sort_by_inter(TSortByFunc func) {
1318 using UO =
typename component_type_t<T>::TypeOriginal;
1319 if constexpr (std::is_same_v<UO, Entity>) {
1320 sort_by_inter(EntityBad, func);
1322 static_assert(core::is_raw_v<UO>,
"Use changed() with raw types only");
1325 const auto& desc = comp_cache_add<T>(*m_storage.
world());
1327 sort_by_inter(desc.entity, func);
1331 template <
typename Rel,
typename Tgt>
1332 void sort_by_inter(TSortByFunc func) {
1333 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
1334 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
1335 static_assert(core::is_raw_v<UO_Rel>,
"Use group_by() with raw types only");
1336 static_assert(core::is_raw_v<UO_Tgt>,
"Use group_by() with raw types only");
1339 const auto& descRel = comp_cache_add<Rel>(*m_storage.
world());
1340 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.
world());
1342 sort_by_inter({descRel.entity, descTgt.entity}, func);
1347 void group_by_inter(Entity entity, TGroupByFunc func) {
1348 QueryCmd_GroupBy cmd{entity, func};
1352 template <
typename T>
1353 void group_by_inter(Entity entity, TGroupByFunc func) {
1354 using UO =
typename component_type_t<T>::TypeOriginal;
1355 static_assert(core::is_raw_v<UO>,
"Use changed() with raw types only");
1357 group_by_inter(entity, func);
1360 template <
typename Rel,
typename Tgt>
1361 void group_by_inter(TGroupByFunc func) {
1362 using UO_Rel =
typename component_type_t<Rel>::TypeOriginal;
1363 using UO_Tgt =
typename component_type_t<Tgt>::TypeOriginal;
1364 static_assert(core::is_raw_v<UO_Rel>,
"Use group_by() with raw types only");
1365 static_assert(core::is_raw_v<UO_Tgt>,
"Use group_by() with raw types only");
1368 const auto& descRel = comp_cache_add<Rel>(*m_storage.
world());
1369 const auto& descTgt = comp_cache_add<Tgt>(*m_storage.
world());
1371 group_by_inter({descRel.entity, descTgt.entity}, func);
1376 void group_dep_inter(Entity relation) {
1377 GAIA_ASSERT(!relation.pair());
1378 QueryCmd_GroupDep cmd{relation};
1382 template <
typename T>
1383 void group_dep_inter() {
1384 using UO =
typename component_type_t<T>::TypeOriginal;
1385 static_assert(core::is_raw_v<UO>,
"Use group_dep() with raw types only");
1387 const auto& desc = comp_cache_add<T>(*m_storage.
world());
1388 group_dep_inter(desc.entity);
1393 void set_group_id_inter(GroupId groupId) {
1397 invalidate_each_walk_cache();
1398 m_groupIdSet = groupId;
1401 void set_group_id_inter(Entity groupId) {
1402 set_group_id_inter(groupId.id());
1405 template <
typename T>
1406 void set_group_id_inter() {
1407 using UO =
typename component_type_t<T>::TypeOriginal;
1408 static_assert(core::is_raw_v<UO>,
"Use group_id() with raw types only");
1411 const auto& desc = comp_cache_add<T>(*m_storage.
world());
1412 set_group_id_inter(desc.entity);
1417 void commit(QueryCtx& ctx) {
1418 GAIA_PROF_SCOPE(query::commit);
1420#if GAIA_ASSERT_ENABLED
1428 while (serBuffer.tell() < serBuffer.bytes()) {
1430 bool invalidatesHash =
false;
1431 ser::load(serBuffer,
id);
1432 ser::load(serBuffer, invalidatesHash);
1433 (void)invalidatesHash;
1434 CommandBufferRead[
id](serBuffer, ctx);
1438 if (uses_query_cache_storage()) {
1439 ctx.data.cacheSrcTrav = m_cacheSrcTrav;
1440 normalize_cache_src_trav(ctx);
1442 if (uses_shared_cache_layer()) {
1443 auto& ctxData = ctx.data;
1444 if (ctxData.changedCnt > 1) {
1445 core::sort(ctxData.changed.data(), ctxData.changed.data() + ctxData.changedCnt, SortComponentCond{});
1454 if (uses_shared_cache_layer())
1455 calc_lookup_hash(ctx);
1458 void recommit(QueryCtx& ctx) {
1459 GAIA_PROF_SCOPE(query::recommit);
1465 while (serBuffer.tell() < serBuffer.bytes()) {
1467 bool invalidatesHash =
false;
1468 ser::load(serBuffer,
id);
1469 ser::load(serBuffer, invalidatesHash);
1471 GAIA_ASSERT(!invalidatesHash);
1472 if (invalidatesHash)
1474 CommandBufferRead[
id](serBuffer, ctx);
1476 if (uses_query_cache_storage()) {
1477 ctx.data.cacheSrcTrav = m_cacheSrcTrav;
1478 normalize_cache_src_trav(ctx);
1487#if GAIA_ASSERT_ENABLED
1489 template <
typename... T>
1490 GAIA_NODISCARD
bool unpack_args_into_query_has_all(
1491 const QueryInfo& queryInfo, [[maybe_unused]] core::func_type_list<T...> types)
const {
1492 if constexpr (
sizeof...(T) > 0)
1493 return queryInfo.template has_all<T...>();
1501 GAIA_NODISCARD
static bool
1502 match_filters(
const Chunk& chunk,
const QueryInfo& queryInfo, uint32_t changedWorldVersion) {
1503 GAIA_ASSERT(!chunk.empty() &&
"match_filters called on an empty chunk");
1505 const auto queryVersion = changedWorldVersion;
1506 const auto& filtered = queryInfo.ctx().data.changed_view();
1509 if (filtered.empty())
1512 const auto filteredCnt = (uint32_t)filtered.size();
1513 auto ids = chunk.ids_view();
1516 if (filteredCnt == 1) {
1517 const auto compIdx = core::get_index(ids, filtered[0]);
1518 if (compIdx != BadIndex && chunk.changed(queryVersion, compIdx))
1521 return chunk.changed(changedWorldVersion);
1525 uint32_t lastIdx = 0;
1526 for (
const auto comp: filtered) {
1527 uint32_t compIdx = BadIndex;
1528 if (lastIdx < (uint32_t)ids.size()) {
1529 const auto suffixIdx =
1531 if (suffixIdx != BadIndex)
1532 compIdx = lastIdx + suffixIdx;
1537 if (compIdx == BadIndex)
1538 compIdx = core::get_index(ids, comp);
1539 if (compIdx == BadIndex)
1542 if (chunk.changed(queryVersion, compIdx))
1550 return chunk.changed(changedWorldVersion);
1553 GAIA_NODISCARD
bool can_process_archetype(
const QueryInfo& queryInfo,
const Archetype& archetype)
const {
1555 if (archetype.is_req_del())
1560 if (!queryInfo.matches_prefab_entities() && archetype.has(Prefab))
1566 GAIA_NODISCARD
static bool has_depth_order_hierarchy_enabled_barrier(
const QueryInfo& queryInfo) {
1567 const auto& data = queryInfo.ctx().data;
1568 return data.groupByFunc == group_by_func_depth_order &&
1569 world_depth_order_prunes_disabled_subtrees(*queryInfo.world(), data.groupBy);
1578 GAIA_NODISCARD
static bool
1580 if (!has_depth_order_hierarchy_enabled_barrier(queryInfo))
1583 const auto& world = *queryInfo.world();
1584 const auto relation = queryInfo.ctx().data.
groupBy;
1585 auto ids = archetype.ids_view();
1587 for (
auto idsIdx: archetype.pair_rel_indices(relation)) {
1588 const auto pair = ids[idsIdx];
1589 const auto parent = world_pair_target_if_alive(world,
pair);
1590 if (parent == EntityBad)
1592 if (!world_entity_enabled_hierarchy(world, parent, relation))
1599 template <
typename TIter>
1600 GAIA_NODISCARD
bool can_process_archetype_inter(
1601 const QueryInfo& queryInfo,
const Archetype& archetype, int8_t barrierPasses = -1)
const {
1602 if (!can_process_archetype(queryInfo, archetype))
1604 if constexpr (std::is_same_v<TIter, Iter>) {
1605 if (has_depth_order_hierarchy_enabled_barrier(queryInfo)) {
1606 if (barrierPasses >= 0)
1607 return barrierPasses != 0;
1615 template <
typename T>
1616 GAIA_NODISCARD
static constexpr bool is_write_query_arg() {
1617 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
1618 return std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>> &&
1619 !std::is_same_v<Arg, Entity>;
1622 template <
typename TIter>
1623 static void finish_iter_writes(TIter& it) {
1624 if (it.chunk() ==
nullptr)
1627 auto compIndices = it.touched_comp_indices();
1628 for (
auto compIdx: compIndices)
1629 const_cast<Chunk*>(it.chunk())->finish_write(compIdx, it.row_begin(), it.row_end());
1631 auto terms = it.touched_terms();
1635 auto entities = it.entity_rows();
1636 auto& world = *it.world();
1638 const auto term = terms[i];
1639 if (!world_is_out_of_line_component(world, term)) {
1640 const auto compIdx = core::get_index(it.chunk()->ids_view(), term);
1641 if (compIdx != BadIndex) {
1642 const_cast<Chunk*
>(it.chunk())->finish_write(compIdx, it.row_begin(), it.row_end());
1647 GAIA_FOR_(entities.size(), j) {
1648 world_finish_write(world, term, entities[j]);
1653 template <
typename... T>
1654 static void finish_typed_chunk_writes(World& world, Chunk* pChunk, uint16_t from, uint16_t to) {
1658 Entity seenTerms[
sizeof...(T) > 0 ?
sizeof...(T) : 1]{};
1659 uint32_t seenCnt = 0;
1660 const auto entities = pChunk->entity_view();
1661 const auto finish_term = [&](Entity term) {
1663 if (seenTerms[i] == term)
1667 seenTerms[seenCnt++] = term;
1668 if (!world_is_out_of_line_component(world, term)) {
1669 const auto compIdx = core::get_index(pChunk->ids_view(), term);
1670 if (compIdx != BadIndex) {
1671 pChunk->finish_write(compIdx, from, to);
1676 for (uint16_t row = from; row < to; ++row)
1677 world_finish_write(world, term, entities[row]);
1682 if constexpr (is_write_query_arg<T>()) {
1683 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
1684 finish_term(world_query_arg_id<Arg>(world));
1690 template <
typename T,
typename TIter>
1691 static void finish_typed_iter_write_arg(TIter& it, uint32_t fieldIdx) {
1692 if constexpr (!is_write_query_arg<T>())
1695 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
1696 auto* pChunk =
const_cast<Chunk*
>(it.chunk());
1697 auto& world = *it.world();
1698 Entity term = it.term_ids() !=
nullptr ? it.term_ids()[fieldIdx] : world_query_arg_id<Arg>(world);
1699 if (term == EntityBad)
1700 term = world_query_arg_id<Arg>(world);
1702 if (it.row_begin() >= it.row_end())
1705 auto compIdx = uint8_t(0xFF);
1707 compIdx = it.comp_indices()[fieldIdx];
1708 else if (!world_is_out_of_line_component(world, term))
1709 compIdx = (uint8_t)pChunk->comp_idx(term);
1710 if (compIdx != 0xFF && !world_is_out_of_line_component(world, term)) {
1711 pChunk->finish_write(compIdx, it.row_begin(), it.row_end());
1715 auto entities = it.entity_rows();
1716 GAIA_FOR(entities.size()) {
1717 world_finish_write(world, term, entities[i]);
1722 template <
typename TIter,
typename... T,
size_t... I>
1723 static void finish_typed_iter_writes(TIter& it, std::index_sequence<I...>) {
1724 (finish_typed_iter_write_arg<T>(it, (uint32_t)I), ...);
1727 enum class ExecPayloadKind : uint8_t { Plain, Grouped, NonTrivial };
1729 template <
typename TIter>
1730 GAIA_NODISCARD
static ExecPayloadKind exec_payload_kind(
const QueryInfo& queryInfo) {
1731 if (queryInfo.has_sorted_payload())
1732 return ExecPayloadKind::NonTrivial;
1733 if (!queryInfo.has_grouped_payload())
1734 return ExecPayloadKind::Plain;
1735 if constexpr (std::is_same_v<TIter, Iter>) {
1736 if (has_depth_order_hierarchy_enabled_barrier(queryInfo))
1737 return ExecPayloadKind::NonTrivial;
1739 return ExecPayloadKind::Grouped;
1742 template <
typename TIter>
1743 GAIA_NODISCARD
static bool needs_nontrivial_payload(
const QueryInfo& queryInfo) {
1744 return exec_payload_kind<TIter>(queryInfo) == ExecPayloadKind::NonTrivial;
1750 template <
typename Func,
typename TIter>
1753 it.set_world(pWorld);
1754 it.set_write_im(
false);
1755 it.set_archetype(batch.pArchetype);
1756 it.set_chunk(batch.pChunk, batch.from, batch.to);
1757 it.set_group_id(batch.groupId);
1758 it.set_comp_indices(batch.pCompIndices);
1759 it.set_inherited_data(batch.inheritedData);
1761 finish_iter_writes(it);
1762 it.clear_touched_writes();
1766 template <
typename Func,
typename TIter>
1769 it.set_world(pWorld);
1770 it.set_write_im(
false);
1771 it.set_archetype(batch.pArchetype);
1773 it.set_group_id(batch.groupId);
1774 it.set_comp_indices(batch.pCompIndices);
1775 it.set_inherited_data(batch.inheritedData);
1777 it.clear_touched_writes();
1781 template <
typename Func,
typename TIter>
1783 GAIA_PROF_SCOPE(query::run_query_func);
1785 const auto chunkCnt = batches.size();
1786 GAIA_ASSERT(chunkCnt > 0);
1789 it.set_world(pWorld);
1790 it.set_write_im(
false);
1792 const Archetype* pLastArchetype =
nullptr;
1793 const uint8_t* pLastIndices =
nullptr;
1794 InheritedTermDataView lastInheritedData{};
1795 GroupId lastGroupId = GroupIdMax;
1797 const auto apply_batch = [&](
const ChunkBatch& batch) {
1798 if (batch.pArchetype != pLastArchetype) {
1799 it.set_archetype(batch.pArchetype);
1800 pLastArchetype = batch.pArchetype;
1803 if (batch.pCompIndices != pLastIndices) {
1804 it.set_comp_indices(batch.pCompIndices);
1805 pLastIndices = batch.pCompIndices;
1808 if (batch.inheritedData.data() != lastInheritedData.data()) {
1809 it.set_inherited_data(batch.inheritedData);
1810 lastInheritedData = batch.inheritedData;
1813 if (batch.groupId != lastGroupId) {
1814 it.set_group_id(batch.groupId);
1815 lastGroupId = batch.groupId;
1818 it.set_chunk(batch.pChunk, batch.from, batch.to);
1820 finish_iter_writes(it);
1821 it.clear_touched_writes();
1825 if GAIA_UNLIKELY (chunkCnt == 1) {
1826 apply_batch(batches[0]);
1839 apply_batch(batches[0]);
1841 uint32_t chunkIdx = 1;
1842 for (; chunkIdx < chunkCnt - 1; ++chunkIdx) {
1844 apply_batch(batches[chunkIdx]);
1847 apply_batch(batches[chunkIdx]);
1852 template <
bool HasFilters,
typename TIter,
typename Func>
1853 void run_query_batch_no_group_id(
1854 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
1855 GAIA_PROF_SCOPE(query::run_query_batch_no_group_id);
1857 auto cacheView = queryInfo.cache_archetype_view();
1858 const auto payloadKind = exec_payload_kind<TIter>(queryInfo);
1859 auto sortView = queryInfo.cache_sort_view();
1860 if (payloadKind != ExecPayloadKind::NonTrivial)
1862 const bool hasInheritedData = queryInfo.has_inherited_data_payload();
1863 const bool needsBarrierCache = payloadKind == ExecPayloadKind::NonTrivial && std::is_same_v<TIter, Iter> &&
1864 has_depth_order_hierarchy_enabled_barrier(queryInfo);
1865 if (needsBarrierCache)
1866 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
1868 lock(*m_storage.
world());
1872 ChunkBatchArray chunkBatches;
1874 if (!sortView.empty()) {
1875 for (
const auto& view: sortView) {
1876 const auto chunkEntitiesCnt = TIter::size(view.pChunk);
1877 if GAIA_UNLIKELY (chunkEntitiesCnt == 0)
1880 const auto viewFrom = view.startRow;
1881 const auto viewTo = (uint16_t)(view.startRow + view.
count);
1883 const auto minStartRow = TIter::start_index(view.pChunk);
1884 const auto minEndRow = TIter::end_index(view.pChunk);
1885 const auto startRow = core::get_max(minStartRow, viewFrom);
1886 const auto endRow = core::get_min(minEndRow, viewTo);
1887 const auto totalRows = endRow - startRow;
1891 if constexpr (HasFilters) {
1892 if (!match_filters(*view.pChunk, queryInfo, m_changedWorldVersion))
1896 auto* pArchetype =
const_cast<Archetype*
>(cacheView[view.archetypeIdx]);
1897 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(view.archetypeIdx);
1898 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
1901 const auto inheritedDataView =
1902 hasInheritedData ? queryInfo.inherited_data_view(view.archetypeIdx) : InheritedTermDataView{};
1904 chunkBatches.push_back(
1905 {pArchetype, view.pChunk, indicesView.data(), inheritedDataView, 0U, startRow, endRow});
1907 if GAIA_UNLIKELY (chunkBatches.size() == chunkBatches.max_size()) {
1908 run_query_func<Func, TIter>(m_storage.
world(), func, {chunkBatches.data(), chunkBatches.size()});
1909 chunkBatches.clear();
1913 for (uint32_t i = idxFrom; i < idxTo; ++i) {
1914 auto* pArchetype =
const_cast<Archetype*
>(cacheView[i]);
1915 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
1916 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
1920 const auto inheritedDataView =
1921 hasInheritedData ? queryInfo.inherited_data_view(i) : InheritedTermDataView{};
1922 const auto& chunks = pArchetype->chunks();
1923 uint32_t chunkOffset = 0;
1924 uint32_t itemsLeft = chunks.size();
1925 while (itemsLeft > 0) {
1926 const auto maxBatchSize = chunkBatches.max_size() - chunkBatches.size();
1927 const auto batchSize = itemsLeft > maxBatchSize ? maxBatchSize : itemsLeft;
1929 ChunkSpanMut chunkSpan((Chunk**)&chunks[chunkOffset], batchSize);
1930 for (
auto* pChunk: chunkSpan) {
1931 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
1934 if constexpr (HasFilters) {
1935 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
1939 chunkBatches.push_back({pArchetype, pChunk, indicesView.data(), inheritedDataView, 0, 0, 0});
1942 if GAIA_UNLIKELY (chunkBatches.size() == chunkBatches.max_size()) {
1943 run_query_func<Func, TIter>(m_storage.
world(), func, {chunkBatches.data(), chunkBatches.size()});
1944 chunkBatches.clear();
1947 itemsLeft -= batchSize;
1948 chunkOffset += batchSize;
1954 if (!chunkBatches.empty())
1955 run_query_func<Func, TIter>(m_storage.
world(), func, {chunkBatches.data(), chunkBatches.size()});
1957 unlock(*m_storage.
world());
1960 commit_cmd_buffer_st(*m_storage.
world());
1961 commit_cmd_buffer_mt(*m_storage.
world());
1964 template <
bool HasFilters,
typename TIter,
typename Func, QueryExecType ExecType>
1965 void run_query_batch_no_group_id_par(
1966 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
1967 static_assert(ExecType != QueryExecType::Default);
1968 GAIA_PROF_SCOPE(query::run_query_batch_no_group_id_par);
1970 auto cacheView = queryInfo.cache_archetype_view();
1971 const auto payloadKind = exec_payload_kind<TIter>(queryInfo);
1972 auto sortView = queryInfo.cache_sort_view();
1973 if (payloadKind != ExecPayloadKind::NonTrivial)
1975 const bool hasInheritedData = queryInfo.has_inherited_data_payload();
1976 const bool needsBarrierCache = payloadKind == ExecPayloadKind::NonTrivial && std::is_same_v<TIter, Iter> &&
1977 has_depth_order_hierarchy_enabled_barrier(queryInfo);
1978 if (needsBarrierCache)
1979 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
1981 if (!sortView.empty()) {
1982 for (
const auto& view: sortView) {
1983 const auto chunkEntitiesCnt = TIter::size(view.pChunk);
1984 if GAIA_UNLIKELY (chunkEntitiesCnt == 0)
1987 const auto viewFrom = view.startRow;
1988 const auto viewTo = (uint16_t)(view.startRow + view.
count);
1990 const auto minStartRow = TIter::start_index(view.pChunk);
1991 const auto minEndRow = TIter::end_index(view.pChunk);
1992 const auto startRow = core::get_max(minStartRow, viewFrom);
1993 const auto endRow = core::get_min(minEndRow, viewTo);
1994 const auto totalRows = endRow - startRow;
1998 if constexpr (HasFilters) {
1999 if (!match_filters(*view.pChunk, queryInfo, m_changedWorldVersion))
2003 const auto* pArchetype = cacheView[view.archetypeIdx];
2004 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(view.archetypeIdx);
2005 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
2007 auto indicesView = queryInfo.indices_mapping_view(view.archetypeIdx);
2008 const auto inheritedDataView =
2009 hasInheritedData ? queryInfo.inherited_data_view(view.archetypeIdx) : InheritedTermDataView{};
2011 m_batches.push_back(
2012 {pArchetype, view.pChunk, indicesView.data(), inheritedDataView, 0U, startRow, endRow});
2015 for (uint32_t i = idxFrom; i < idxTo; ++i) {
2016 const auto* pArchetype = cacheView[i];
2017 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
2018 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
2021 auto indicesView = queryInfo.indices_mapping_view(i);
2022 const auto inheritedDataView =
2023 hasInheritedData ? queryInfo.inherited_data_view(i) : InheritedTermDataView{};
2024 const auto& chunks = pArchetype->chunks();
2025 for (
auto* pChunk: chunks) {
2026 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
2029 if constexpr (HasFilters) {
2030 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
2034 m_batches.push_back({pArchetype, pChunk, indicesView.data(), inheritedDataView, 0, 0, 0});
2039 if (m_batches.empty())
2042 lock(*m_storage.
world());
2047 if constexpr (ExecType == QueryExecType::ParallelEff)
2048 j.priority = mt::JobPriority::Low;
2050 j.func = [&](
const mt::JobArgs& args) {
2051 run_query_func<Func, TIter>(
2052 m_storage.
world(), func,
std::span(&m_batches[args.idxStart], args.idxEnd - args.idxStart));
2055 auto& tp = mt::ThreadPool::get();
2056 auto jobHandle = tp.sched_par(j, m_batches.size(), 0);
2060 unlock(*m_storage.
world());
2063 commit_cmd_buffer_st(*m_storage.
world());
2064 commit_cmd_buffer_mt(*m_storage.
world());
2067 template <
bool HasFilters,
typename TIter,
typename Func>
2068 void run_query_batch_with_group_id(
2069 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
2070 GAIA_PROF_SCOPE(query::run_query_batch_with_group_id);
2072 ChunkBatchArray chunkBatches;
2074 auto cacheView = queryInfo.cache_archetype_view();
2075 const bool hasInheritedData = queryInfo.has_inherited_data_payload();
2076 const bool needsBarrierCache = needs_nontrivial_payload<TIter>(queryInfo) && std::is_same_v<TIter, Iter> &&
2077 has_depth_order_hierarchy_enabled_barrier(queryInfo);
2078 if (needsBarrierCache)
2079 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
2081 lock(*m_storage.
world());
2083 for (uint32_t i = idxFrom; i < idxTo; ++i) {
2084 const auto* pArchetype = cacheView[i];
2085 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
2086 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
2089 auto indicesView = queryInfo.indices_mapping_view(i);
2090 const auto inheritedDataView =
2091 hasInheritedData ? queryInfo.inherited_data_view(i) : InheritedTermDataView{};
2092 const auto& chunks = pArchetype->chunks();
2093 const auto groupId = queryInfo.group_id(i);
2095#if GAIA_ASSERT_ENABLED
2098 m_groupIdSet == 0 ||
2100 groupId == m_groupIdSet);
2103 uint32_t chunkOffset = 0;
2104 uint32_t itemsLeft = chunks.size();
2105 while (itemsLeft > 0) {
2106 const auto maxBatchSize = chunkBatches.max_size() - chunkBatches.size();
2107 const auto batchSize = itemsLeft > maxBatchSize ? maxBatchSize : itemsLeft;
2109 ChunkSpanMut chunkSpan((Chunk**)&chunks[chunkOffset], batchSize);
2110 for (
auto* pChunk: chunkSpan) {
2111 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
2114 if constexpr (HasFilters) {
2115 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
2119 chunkBatches.push_back({pArchetype, pChunk, indicesView.data(), inheritedDataView, groupId, 0, 0});
2122 if GAIA_UNLIKELY (chunkBatches.size() == chunkBatches.max_size()) {
2123 run_query_func<Func, TIter>(m_storage.
world(), func, {chunkBatches.data(), chunkBatches.size()});
2124 chunkBatches.clear();
2127 itemsLeft -= batchSize;
2128 chunkOffset += batchSize;
2133 if (!chunkBatches.empty())
2134 run_query_func<Func, TIter>(m_storage.
world(), func, {chunkBatches.data(), chunkBatches.size()});
2136 unlock(*m_storage.
world());
2139 commit_cmd_buffer_st(*m_storage.
world());
2140 commit_cmd_buffer_mt(*m_storage.
world());
2143 template <
bool HasFilters,
typename TIter,
typename Func, QueryExecType ExecType>
2144 void run_query_batch_with_group_id_par(
2145 const QueryInfo& queryInfo,
const uint32_t idxFrom,
const uint32_t idxTo, Func func) {
2146 static_assert(ExecType != QueryExecType::Default);
2147 GAIA_PROF_SCOPE(query::run_query_batch_with_group_id_par);
2149 ChunkBatchArray chunkBatch;
2151 auto cacheView = queryInfo.cache_archetype_view();
2152 const bool hasInheritedData = queryInfo.has_inherited_data_payload();
2153 const bool needsBarrierCache = needs_nontrivial_payload<TIter>(queryInfo) && std::is_same_v<TIter, Iter> &&
2154 has_depth_order_hierarchy_enabled_barrier(queryInfo);
2155 if (needsBarrierCache)
2156 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
2158#if GAIA_ASSERT_ENABLED
2159 for (uint32_t i = idxFrom; i < idxTo; ++i) {
2160 const auto* pArchetype = cacheView[i];
2161 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
2162 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
2165 const auto groupId = queryInfo.group_id(i);
2168 m_groupIdSet == 0 ||
2170 groupId == m_groupIdSet);
2174 for (uint32_t i = idxFrom; i < idxTo; ++i) {
2175 const Archetype* pArchetype = cacheView[i];
2176 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
2177 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
2180 auto indicesView = queryInfo.indices_mapping_view(i);
2181 const auto inheritedDataView =
2182 hasInheritedData ? queryInfo.inherited_data_view(i) : InheritedTermDataView{};
2183 const auto groupId = queryInfo.group_id(i);
2184 const auto& chunks = pArchetype->chunks();
2185 for (
auto* pChunk: chunks) {
2186 if GAIA_UNLIKELY (TIter::size(pChunk) == 0)
2189 if constexpr (HasFilters) {
2190 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
2194 m_batches.push_back({pArchetype, pChunk, indicesView.data(), inheritedDataView, groupId, 0, 0});
2198 if (m_batches.empty())
2201 lock(*m_storage.
world());
2206 if constexpr (ExecType == QueryExecType::ParallelEff)
2207 j.priority = mt::JobPriority::Low;
2209 j.func = [&](
const mt::JobArgs& args) {
2210 run_query_func<Func, TIter>(
2211 m_storage.
world(), func,
std::span(&m_batches[args.idxStart], args.idxEnd - args.idxStart));
2214 auto& tp = mt::ThreadPool::get();
2215 auto jobHandle = tp.sched_par(j, m_batches.size(), 0);
2219 unlock(*m_storage.
world());
2222 commit_cmd_buffer_st(*m_storage.
world());
2223 commit_cmd_buffer_mt(*m_storage.
world());
2228 template <
bool HasFilters, QueryExecType ExecType,
typename TIter,
typename Func>
2229 void run_query(
const QueryInfo& queryInfo, Func func) {
2230 GAIA_PROF_SCOPE(query::run_query);
2237 auto cache_view = queryInfo.cache_archetype_view();
2238 if (cache_view.empty())
2241 const bool isGroupBy = queryInfo.ctx().data.groupBy != EntityBad;
2242 const bool isGroupSet = m_groupIdSet != 0;
2243 if (!isGroupBy || !isGroupSet) {
2245 const auto idxFrom = 0;
2246 const auto idxTo = (uint32_t)cache_view.size();
2247 if constexpr (ExecType != QueryExecType::Default)
2248 run_query_batch_no_group_id_par<HasFilters, TIter, Func, ExecType>(queryInfo, idxFrom, idxTo, func);
2250 run_query_batch_no_group_id<HasFilters, TIter, Func>(queryInfo, idxFrom, idxTo, func);
2253 const auto* pGroupData = queryInfo.selected_group_data(m_groupIdSet);
2254 if (pGroupData ==
nullptr)
2257 const auto idxFrom = pGroupData->idxFirst;
2258 const auto idxTo = pGroupData->idxLast + 1;
2259 if constexpr (ExecType != QueryExecType::Default)
2260 run_query_batch_with_group_id_par<HasFilters, TIter, Func, ExecType>(queryInfo, idxFrom, idxTo, func);
2262 run_query_batch_with_group_id<HasFilters, TIter, Func>(queryInfo, idxFrom, idxTo, func);
2268 template <QueryExecType ExecType,
typename TIter,
typename Func>
2269 void run_query_on_archetypes(QueryInfo& queryInfo, Func func) {
2273 lock(*m_storage.
world());
2276 GAIA_PROF_SCOPE(query::run_query_a);
2282 auto cache_view = queryInfo.cache_archetype_view();
2283 const bool needsBarrierCache = has_depth_order_hierarchy_enabled_barrier(queryInfo);
2284 const bool hasInheritedData = queryInfo.has_inherited_data_payload();
2285 if (needsBarrierCache)
2286 queryInfo.ensure_depth_order_hierarchy_barrier_cache();
2287 GAIA_EACH(cache_view) {
2288 const auto* pArchetype = cache_view[i];
2289 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(i);
2290 if GAIA_UNLIKELY (!can_process_archetype_inter<Iter>(queryInfo, *pArchetype, barrierPasses))
2293 auto indicesView = queryInfo.indices_mapping_view(i);
2294 const auto inheritedDataView =
2295 hasInheritedData ? queryInfo.inherited_data_view(i) : InheritedTermDataView{};
2296 ChunkBatch batch{pArchetype,
nullptr, indicesView.data(), inheritedDataView, 0, 0, 0};
2297 run_query_arch_func<Func, Iter>(m_storage.
world(), func, batch);
2301 unlock(*m_storage.
world());
2307 template <QueryExecType ExecType,
typename TIter,
typename Func>
2308 void run_query_on_chunks(QueryInfo& queryInfo, Func func) {
2310 ::gaia::ecs::update_version(*m_worldVersion);
2312 const bool hasFilters = queryInfo.has_filters();
2314 run_query<true, ExecType, TIter>(queryInfo, func);
2316 run_query<false, ExecType, TIter>(queryInfo, func);
2319 m_changedWorldVersion = *m_worldVersion;
2322 GAIA_NODISCARD
bool can_use_direct_chunk_iteration_fastpath(
const QueryInfo& queryInfo)
const {
2323 const auto& data = queryInfo.ctx().data;
2324 return data.sortByFunc ==
nullptr && !has_depth_order_hierarchy_enabled_barrier(queryInfo);
2327 template <
typename Func,
typename... T>
2328 void run_query_on_chunks_direct(QueryInfo& queryInfo, Func func, core::func_type_list<T...>) {
2329 if constexpr (has_write_query_args<T...>())
2330 ::gaia::ecs::update_version(*m_worldVersion);
2332 const bool hasFilters = queryInfo.has_filters();
2333 auto cacheView = queryInfo.cache_archetype_view();
2334 if (cacheView.empty())
2337 uint32_t idxFrom = 0;
2338 uint32_t idxTo = (uint32_t)cacheView.size();
2339 if (queryInfo.ctx().data.groupBy != EntityBad && m_groupIdSet != 0) {
2340 const auto* pGroupData = queryInfo.selected_group_data(m_groupIdSet);
2341 if (pGroupData ==
nullptr)
2343 idxFrom = pGroupData->idxFirst;
2344 idxTo = pGroupData->idxLast + 1;
2347 lock(*m_storage.
world());
2349 for (uint32_t i = idxFrom; i < idxTo; ++i) {
2350 const auto* pArchetype = cacheView[i];
2351 if GAIA_UNLIKELY (!can_process_archetype_inter<Iter>(queryInfo, *pArchetype))
2354 const auto& chunks = pArchetype->chunks();
2355 for (
auto* pChunk: chunks) {
2356 const auto from = Iter::start_index(pChunk);
2357 const auto to = Iter::end_index(pChunk);
2358 if GAIA_UNLIKELY (from == to)
2362 if GAIA_UNLIKELY (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
2366 GAIA_PROF_SCOPE(query_func);
2367 run_query_on_chunk_rows_direct(pChunk, from, to, func, core::func_type_list<T...>{});
2368 finish_typed_chunk_writes<T...>(*queryInfo.world(), pChunk, from, to);
2372 unlock(*m_storage.
world());
2373 commit_cmd_buffer_st(*m_storage.
world());
2374 commit_cmd_buffer_mt(*m_storage.
world());
2375 m_changedWorldVersion = *m_worldVersion;
2378 template <
typename TIter,
typename Func,
typename... T>
2379 GAIA_FORCEINLINE
void
2380 run_query_on_chunk(TIter& it, Func func, [[maybe_unused]] core::func_type_list<T...> types) {
2381 auto& queryInfo =
fetch();
2382 auto& world = *
const_cast<World*
>(queryInfo.world());
2383 if (can_use_direct_chunk_term_eval<T...>(world, queryInfo))
2384 run_query_on_chunk_direct(it, func, types);
2386 run_query_on_chunk(queryInfo, it, func, types);
2389 template <
typename TIter,
typename Func,
typename... T>
2390 GAIA_FORCEINLINE
void run_query_on_chunk(
2391 const QueryInfo& queryInfo, TIter& it, Func func, [[maybe_unused]] core::func_type_list<T...> types) {
2392 const auto cnt = it.size();
2393 const bool hasEntityFilters = queryInfo.has_entity_filter_terms();
2395 if constexpr (
sizeof...(T) > 0) {
2401 auto dataPointerTuple = std::make_tuple(it.template view_auto_any<T>()...);
2407 if (!hasEntityFilters) {
2410 std::get<
decltype(it.template view_auto_any<T>())>(
2411 dataPointerTuple)[it.template acc_index<T>(i)]...);
2414 const auto entities = it.template view<Entity>();
2419 std::get<
decltype(it.template view_auto_any<T>())>(
2420 dataPointerTuple)[it.template acc_index<T>(i)]...);
2425 if (!hasEntityFilters) {
2430 const auto entities = it.template view<Entity>();
2439 finish_typed_iter_writes<TIter, T...>(it, std::index_sequence_for<T...>{});
2440 it.clear_touched_writes();
2443 template <
typename TIter,
typename Func,
typename... T>
2444 GAIA_FORCEINLINE
void
2445 run_query_on_chunk_direct(TIter& it, Func func, [[maybe_unused]] core::func_type_list<T...> types) {
2446 run_query_on_chunk_rows_direct(
const_cast<Chunk*
>(it.chunk()), it.row_begin(), it.row_end(), func, types);
2447 finish_typed_iter_writes<TIter, T...>(it, std::index_sequence_for<T...>{});
2448 it.clear_touched_writes();
2451 template <
typename... T>
2452 GAIA_NODISCARD
static constexpr bool has_write_query_args() {
2453 return (is_write_query_arg<T>() || ...);
2456 template <
typename T>
2457 GAIA_FORCEINLINE
static decltype(
auto) chunk_view_auto(Chunk* pChunk) {
2458 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
2459 if constexpr (std::is_same_v<Arg, Entity>)
2460 return pChunk->entity_view();
2462 using FT =
typename component_type_t<Arg>::TypeFull;
2463 if constexpr (std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>)
2464 return pChunk->template sview_mut<FT>();
2466 return pChunk->template view<FT>();
2470 template <
typename Func,
typename... T>
2471 GAIA_FORCEINLINE
static void run_query_on_chunk_rows_direct(
2472 Chunk* pChunk, uint16_t from, uint16_t to, Func& func, core::func_type_list<T...>) {
2473 if constexpr (
sizeof...(T) > 0) {
2474 auto dataPointerTuple = std::make_tuple(chunk_view_auto<T>(pChunk)...);
2475 for (uint16_t row = from; row < to; ++row)
2476 func(std::get<
decltype(chunk_view_auto<T>(pChunk))>(dataPointerTuple)[row]...);
2478 for (uint16_t row = from; row < to; ++row)
2483 template <
typename TIter,
typename Func,
typename... T>
2484 GAIA_FORCEINLINE
void
2485 run_query_on_direct_entity(TIter& it, Func func, [[maybe_unused]] core::func_type_list<T...> types) {
2486 if constexpr (
sizeof...(T) > 0) {
2487 auto dataPointerTuple = std::make_tuple(it.template view_auto_any<T>()...);
2488 func(std::get<
decltype(it.template view_auto_any<T>())>(dataPointerTuple)[it.template acc_index<T>(0)]...);
2493 finish_typed_iter_writes<TIter, T...>(it, std::index_sequence_for<T...>{});
2494 it.clear_touched_writes();
2497 template <
typename TIter,
typename Func,
typename... T>
2498 GAIA_FORCEINLINE
void
2499 run_query_on_direct_entity_direct(TIter& it, Func func, [[maybe_unused]] core::func_type_list<T...> types) {
2500 run_query_on_chunk_rows_direct(
const_cast<Chunk*
>(it.chunk()), it.row_begin(), it.row_end(), func, types);
2501 finish_typed_iter_writes<TIter, T...>(it, std::index_sequence_for<T...>{});
2502 it.clear_touched_writes();
2507 template <QueryExecType ExecType,
typename Func,
typename... T>
2508 void each_inter(QueryInfo& queryInfo, Func func, core::func_type_list<T...>) {
2510 GAIA_PROF_SCOPE(query_func);
2511 each_direct_inter<Iter>(queryInfo, func, core::func_type_list<T...>{});
2515 auto& world = *
const_cast<World*
>(queryInfo.world());
2516 if (can_use_direct_chunk_term_eval<T...>(world, queryInfo)) {
2517 if constexpr (ExecType == QueryExecType::Default) {
2518 if (can_use_direct_chunk_iteration_fastpath(queryInfo)) {
2519 run_query_on_chunks_direct(queryInfo, func, core::func_type_list<T...>{});
2523 run_query_on_chunks<ExecType, Iter>(queryInfo, [&](Iter& it) {
2524 GAIA_PROF_SCOPE(query_func);
2525 run_query_on_chunk_direct(it, func, core::func_type_list<T...>{});
2528 run_query_on_chunks<ExecType, Iter>(queryInfo, [&](Iter& it) {
2529 GAIA_PROF_SCOPE(query_func);
2530 run_query_on_chunk(queryInfo, it, func, core::func_type_list<T...>{});
2535 template <QueryExecType ExecType,
typename Func>
2536 void each_inter(QueryInfo& queryInfo, Func func) {
2537 using InputArgs =
decltype(core::func_args(&Func::operator()));
2539#if GAIA_ASSERT_ENABLED
2554 GAIA_ASSERT(unpack_args_into_query_has_all(queryInfo, InputArgs{}));
2557 each_inter<ExecType>(queryInfo, func, InputArgs{});
2560 template <QueryExecType ExecType,
typename Func>
2561 void each_inter(Func func) {
2562 auto& queryInfo =
fetch();
2565 if constexpr (std::is_invocable_v<Func, IterAll&>) {
2567 GAIA_PROF_SCOPE(query_func);
2568 each_direct_iter_inter<IterAll>(queryInfo, func);
2571 run_query_on_chunks<ExecType, IterAll>(queryInfo, [&](IterAll& it) {
2572 GAIA_PROF_SCOPE(query_func);
2575 }
else if constexpr (std::is_invocable_v<Func, Iter&>) {
2577 GAIA_PROF_SCOPE(query_func);
2578 each_direct_iter_inter<Iter>(queryInfo, func);
2581 run_query_on_chunks<ExecType, Iter>(queryInfo, [&](Iter& it) {
2582 GAIA_PROF_SCOPE(query_func);
2585 }
else if constexpr (std::is_invocable_v<Func, IterDisabled&>) {
2587 GAIA_PROF_SCOPE(query_func);
2588 each_direct_iter_inter<IterDisabled>(queryInfo, func);
2591 run_query_on_chunks<ExecType, IterDisabled>(queryInfo, [&](IterDisabled& it) {
2592 GAIA_PROF_SCOPE(query_func);
2596 each_inter<ExecType>(queryInfo, func);
2606 if (term.
src != EntityBad || term.
entTrav != EntityBad || term_has_variables(term))
2609 const auto id = term.
id;
2610 return (
id.
pair() && world_is_exclusive_dont_fragment_relation(world, entity_from_id(world,
id.
id()))) ||
2611 (!
id.pair() && world_is_non_fragmenting_out_of_line_component(world,
id));
2618 const auto id = term.
id;
2619 return term.
matchKind == QueryMatchKind::Semantic && term.
src == EntityBad && term.
entTrav == EntityBad &&
2620 !term_has_variables(term) &&
id.pair() &&
id.id() == Is.id() && !is_wildcard(
id.
gen()) &&
2621 !is_variable((EntityId)
id.
gen());
2628 const auto id = term.
id;
2629 return term.
matchKind == QueryMatchKind::In && term.
src == EntityBad && term.
entTrav == EntityBad &&
2630 !term_has_variables(term) &&
id.pair() &&
id.id() == Is.id() && !is_wildcard(
id.
gen()) &&
2631 !is_variable((EntityId)
id.
gen());
2646 const auto id = term.
id;
2647 return term.
matchKind == QueryMatchKind::Semantic && term.
src == EntityBad && term.
entTrav == EntityBad &&
2648 !term_has_variables(term) && !is_wildcard(
id) && !is_variable((EntityId)
id.
id()) &&
2649 (!
id.pair() || !is_variable((EntityId)
id.
gen())) && world_term_uses_inherit_policy(world,
id);
2655 return world_has_entity_term(world, entity, term.
id);
2657 return world_has_entity_term_in(world, entity, term.
id);
2659 return world_has_entity_term_direct(world, entity, term.
id);
2666 case QueryCtx::DirectTargetEvalKind::SingleAllSemanticIs:
2667 case QueryCtx::DirectTargetEvalKind::SingleAllInherited:
2668 return world_has_entity_term(world, entity, termId);
2669 case QueryCtx::DirectTargetEvalKind::SingleAllInIs:
2670 return world_has_entity_term_in(world, entity, termId);
2671 case QueryCtx::DirectTargetEvalKind::SingleAllDirect:
2672 return world_has_entity_term_direct(world, entity, termId);
2673 case QueryCtx::DirectTargetEvalKind::Generic:
2680 GAIA_NODISCARD
static uint32_t count_direct_term_entities(
const World& world,
const QueryTerm& term) {
2682 return world_count_direct_term_entities(world, term.
id);
2684 return world_count_in_term_entities(world, term.
id);
2686 return world_count_direct_term_entities_direct(world, term.
id);
2691 world_collect_direct_term_entities(world, term.
id, out);
2695 world_collect_in_term_entities(world, term.
id, out);
2699 world_collect_direct_term_entities_direct(world, term.
id, out);
2702 template <
typename Func>
2703 GAIA_NODISCARD
static bool for_each_direct_term_entity(
const World& world,
const QueryTerm& term, Func&& func) {
2706 static bool thunk(
void* ctx, Entity entity) {
2707 return static_cast<Visitor*
>(ctx)->func(entity);
2711 Visitor visitor{func};
2713 return world_for_each_direct_term_entity(world, term.id, &visitor, &Visitor::thunk);
2715 return world_for_each_in_term_entity(world, term.id, &visitor, &Visitor::thunk);
2717 return world_for_each_direct_term_entity_direct(world, term.id, &visitor, &Visitor::thunk);
2722 const auto& ctxData = queryInfo.ctx().data;
2723 if (ctxData.sortByFunc !=
nullptr || ctxData.groupBy != EntityBad)
2726 const auto& world = *queryInfo.world();
2727 bool hasPositiveTerm =
false;
2728 bool hasSeedableTerm =
false;
2729 for (
const auto& term: ctxData.terms_view()) {
2730 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2733 if (term.op == QueryOpKind::Any || term.op == QueryOpKind::Count)
2736 if (term.op == QueryOpKind::All || term.op == QueryOpKind::Or) {
2737 hasPositiveTerm =
true;
2740 hasSeedableTerm =
true;
2745 return hasPositiveTerm && hasSeedableTerm;
2750 bool hasPositiveTerm =
false;
2751 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2752 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2755 if (term.op == QueryOpKind::Any || term.op == QueryOpKind::Count)
2758 if (term.op == QueryOpKind::All || term.op == QueryOpKind::Or)
2759 hasPositiveTerm =
true;
2762 return hasPositiveTerm;
2768 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2769 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2771 if (term.op == QueryOpKind::Or) {
2775 if (term.op != QueryOpKind::Not)
2782 template <
typename TIter>
2783 GAIA_NODISCARD
static constexpr Constraints direct_seed_constraints() {
2784 if constexpr (std::is_same_v<TIter, Iter>)
2785 return Constraints::EnabledOnly;
2786 else if constexpr (std::is_same_v<TIter, IterDisabled>)
2787 return Constraints::DisabledOnly;
2789 return Constraints::AcceptAll;
2799 auto& run = runs.back();
2800 if (ec.
pChunk == run.pChunk && ec.
row == run.to) {
2801 run.to = (uint16_t)(run.to + 1);
2809 Entity seededAllTerm = EntityBad;
2810 QueryMatchKind seededAllMatchKind = QueryMatchKind::Semantic;
2811 bool seededFromAll =
false;
2812 bool seededFromOr =
false;
2817 Entity bestAllTerm = EntityBad;
2818 uint32_t bestAllTermCount = UINT32_MAX;
2819 QueryMatchKind bestAllTermMatchKind = QueryMatchKind::Semantic;
2820 bool hasAllTerms =
false;
2821 bool hasOrTerms =
false;
2822 bool preferOrSeed =
false;
2837 const bool bestIsSemanticIs = plan.bestAllTermMatchKind != QueryMatchKind::Direct &&
2838 plan.bestAllTerm.pair() && plan.bestAllTerm.id() == Is.id() &&
2839 !is_wildcard(plan.bestAllTerm.gen()) &&
2840 !is_variable((EntityId)plan.bestAllTerm.gen());
2841 const auto adjustedCandidateCount = candidateCount - (candidateIsSemanticIs && candidateCount > 0 ? 1U : 0U);
2842 const auto adjustedBestCount =
2843 plan.bestAllTermCount - (bestIsSemanticIs && plan.bestAllTermCount > 0 ? 1U : 0U);
2844 if (adjustedCandidateCount < adjustedBestCount)
2846 if (adjustedCandidateCount > adjustedBestCount)
2848 if (plan.bestAllTerm == EntityBad)
2851 if (candidateIsSemanticIs != bestIsSemanticIs)
2852 return candidateIsSemanticIs;
2855 const bool bestUsesInherited = plan.bestAllTermMatchKind == QueryMatchKind::Semantic &&
2856 !is_wildcard(plan.bestAllTerm) &&
2857 !is_variable((EntityId)plan.bestAllTerm.id()) &&
2858 (!plan.bestAllTerm.pair() || !is_variable((EntityId)plan.bestAllTerm.gen())) &&
2859 world_term_uses_inherit_policy(world, plan.bestAllTerm);
2860 if (candidateUsesInherited != bestUsesInherited)
2861 return !candidateUsesInherited;
2866 GAIA_NODISCARD
static DirectEntitySeedPlan
2867 direct_entity_seed_plan(
const World& world,
const QueryInfo& queryInfo) {
2868 DirectEntitySeedPlan plan;
2869 uint32_t totalOrTermCount = 0;
2871 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2872 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2874 if (term.op == QueryOpKind::All) {
2875 plan.hasAllTerms =
true;
2876 const auto cnt = count_direct_term_entities(world, term);
2878 plan.bestAllTermCount = cnt;
2879 plan.bestAllTerm = term.id;
2880 plan.bestAllTermMatchKind = term.matchKind;
2882 }
else if (term.op == QueryOpKind::Or) {
2883 plan.hasOrTerms =
true;
2884 totalOrTermCount += count_direct_term_entities(world, term);
2888 plan.preferOrSeed = plan.hasOrTerms && (!plan.hasAllTerms || totalOrTermCount < plan.bestAllTermCount);
2895 bool hasOrTerms =
false;
2896 bool anyOrMatched =
false;
2898 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2899 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2901 if (seedInfo.seededFromAll && term.op == QueryOpKind::All && term.id == seedInfo.seededAllTerm &&
2902 term.matchKind == seedInfo.seededAllMatchKind)
2904 if (seedInfo.seededFromOr && term.op == QueryOpKind::Or)
2909 case QueryOpKind::All:
2913 case QueryOpKind::Or:
2915 anyOrMatched |= present;
2917 case QueryOpKind::Not:
2921 case QueryOpKind::Any:
2922 case QueryOpKind::Count:
2927 return !hasOrTerms || anyOrMatched;
2931 find_direct_all_seed_term(
const QueryInfo& queryInfo,
const DirectEntitySeedPlan& plan) {
2932 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2933 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2935 if (term.op != QueryOpKind::All || term.id != plan.bestAllTerm ||
2936 term.matchKind != plan.bestAllTermMatchKind)
2944 GAIA_NODISCARD
static DirectEntitySeedEvalPlan
2945 direct_all_seed_eval_plan(
const QueryInfo& queryInfo,
const DirectEntitySeedInfo& seedInfo) {
2946 DirectEntitySeedEvalPlan plan{};
2948 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2949 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2951 if (seedInfo.seededFromAll && term.op == QueryOpKind::All && term.id == seedInfo.seededAllTerm &&
2952 term.matchKind == seedInfo.seededAllMatchKind)
2955 if (term.op == QueryOpKind::All) {
2956 if (plan.pSingleAllTerm !=
nullptr)
2958 plan.pSingleAllTerm = &term;
2965 plan.alwaysMatch = plan.pSingleAllTerm ==
nullptr;
2974 GAIA_NODISCARD
static bool
2979 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
2980 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
2982 if (term.op == QueryOpKind::Any || term.op == QueryOpKind::Count || term.op == QueryOpKind::Or)
2984 if (term.op == QueryOpKind::All && term.id == seedTerm.
id && term.matchKind == seedTerm.
matchKind)
2993 template <
typename TIter>
2995 cached_direct_seed_runs(
QueryInfo& queryInfo,
const QueryTerm& seedTerm,
const DirectEntitySeedInfo& seedInfo) {
2996 auto& runData = ensure_direct_seed_run_data();
2997 auto& world = *queryInfo.world();
2998 const auto constraints = direct_seed_constraints<TIter>();
2999 const auto relVersion = world_rel_version(world, Is);
3000 const auto worldVersion = ::gaia::ecs::world_version(world);
3002 if (runData.cacheValid && runData.cachedSeedTerm == seedTerm.
id &&
3003 runData.cachedSeedMatchKind == seedTerm.
matchKind && runData.cachedConstraints == constraints &&
3004 runData.cachedRelVersion == relVersion && runData.cachedWorldVersion == worldVersion) {
3005 return {runData.cachedRuns.data(), runData.cachedRuns.size()};
3008 auto& runs = runData.cachedRuns;
3009 auto& entities = runData.cachedEntities;
3010 auto& chunkOrderedEntities = runData.cachedChunkOrderedEntities;
3013 chunkOrderedEntities.clear();
3015 (void)for_each_direct_term_entity(world, seedTerm, [&](Entity entity) {
3016 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
3022 entities.push_back(entity);
3026 chunkOrderedEntities = entities;
3027 core::sort(chunkOrderedEntities, [&](Entity left, Entity right) {
3028 const auto& ecLeft = ::gaia::ecs::fetch(world, left);
3029 const auto& ecRight = ::gaia::ecs::fetch(world, right);
3030 if (ecLeft.pArchetype != ecRight.pArchetype)
3031 return ecLeft.pArchetype->id() < ecRight.pArchetype->id();
3032 if (ecLeft.pChunk != ecRight.pChunk)
3033 return ecLeft.pChunk < ecRight.pChunk;
3034 return ecLeft.row < ecRight.row;
3037 uint32_t entityOffset = 0;
3038 for (
const auto entity: chunkOrderedEntities) {
3039 const auto& ec = ::gaia::ecs::fetch(world, entity);
3040 append_chunk_run(runs, ec, entityOffset++);
3043 runData.cachedSeedTerm = seedTerm.
id;
3044 runData.cachedSeedMatchKind = seedTerm.
matchKind;
3045 runData.cachedConstraints = constraints;
3046 runData.cachedRelVersion = relVersion;
3047 runData.cachedWorldVersion = worldVersion;
3048 runData.cacheValid =
true;
3049 return {runs.data(), runs.size()};
3052 template <
typename TIter>
3054 QueryInfo& queryInfo,
const QueryTerm& seedTerm,
const DirectEntitySeedInfo& seedInfo) {
3055 (void)cached_direct_seed_runs<TIter>(queryInfo, seedTerm, seedInfo);
3056 auto& runData = ensure_direct_seed_run_data();
3057 return {runData.cachedEntities.data(), runData.cachedEntities.size()};
3060 template <
typename TIter>
3062 QueryInfo& queryInfo,
const QueryTerm& seedTerm,
const DirectEntitySeedInfo& seedInfo) {
3063 (void)cached_direct_seed_runs<TIter>(queryInfo, seedTerm, seedInfo);
3064 auto& runData = ensure_direct_seed_run_data();
3065 return {runData.cachedChunkOrderedEntities.data(), runData.cachedChunkOrderedEntities.size()};
3068 template <
typename TIter,
typename Func>
3069 GAIA_NODISCARD
static bool for_each_direct_all_seed(
3070 const World& world,
const QueryInfo& queryInfo,
const DirectEntitySeedPlan& plan, Func&& func) {
3071 const auto* pSeedTerm = find_direct_all_seed_term(queryInfo, plan);
3072 GAIA_ASSERT(pSeedTerm !=
nullptr);
3073 if (pSeedTerm ==
nullptr)
3076 DirectEntitySeedInfo seedInfo{};
3077 seedInfo.seededAllTerm = pSeedTerm->id;
3078 seedInfo.seededAllMatchKind = pSeedTerm->matchKind;
3079 seedInfo.seededFromAll =
true;
3080 const auto evalPlan = direct_all_seed_eval_plan(queryInfo, seedInfo);
3081 const Archetype* pLastSingleAllArchetype =
nullptr;
3082 bool lastSingleAllMatch =
false;
3083 bool seedImpliesSingleAllTerm =
false;
3087 const auto seedTarget = entity_from_id(world, (EntityId)pSeedTerm->id.gen());
3088 if (seedTarget != EntityBad)
3089 seedImpliesSingleAllTerm =
match_entity_term(world, seedTarget, *evalPlan.pSingleAllTerm);
3094 return for_each_direct_term_entity(world, *pSeedTerm, [&](Entity entity) {
3095 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
3098 if (evalPlan.alwaysMatch)
3099 return func(entity);
3100 if (evalPlan.pSingleAllTerm !=
nullptr) {
3101 if (seedImpliesSingleAllTerm)
3102 return func(entity);
3103 if (uses_non_direct_is_matching(*evalPlan.pSingleAllTerm) ||
3104 uses_inherited_id_matching(world, *evalPlan.pSingleAllTerm)) {
3105 const auto* pArchetype = world_entity_archetype(world, entity);
3106 if (pArchetype != pLastSingleAllArchetype) {
3107 lastSingleAllMatch = match_entity_term(world, entity, *evalPlan.pSingleAllTerm);
3108 pLastSingleAllArchetype = pArchetype;
3110 if (!lastSingleAllMatch)
3115 return func(entity);
3120 return func(entity);
3125 template <
typename TIter>
3126 GAIA_NODISCARD
static bool
3131 if constexpr (std::is_same_v<TIter, Iter>)
3132 return world_entity_enabled(world, entity);
3133 else if constexpr (std::is_same_v<TIter, IterDisabled>)
3134 return !world_entity_enabled(world, entity);
3142 if (!seedInfo.seededFromAll && !seedInfo.seededFromOr)
3145 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3146 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
3148 if (seedInfo.seededFromAll && term.id == seedInfo.seededAllTerm && term.op == QueryOpKind::All)
3150 if (seedInfo.seededFromOr && term.op == QueryOpKind::Or)
3152 if (term.op != QueryOpKind::All && term.op != QueryOpKind::Not)
3154 if (is_adjunct_direct_term(world, term))
3156 if (uses_inherited_id_matching(world, term))
3164 template <
typename TIter>
3168 auto& scratch = direct_query_scratch();
3170 scratch.archetypes.clear();
3171 scratch.bucketEntities.clear();
3172 scratch.counts.clear();
3174 for (
const auto entity: seedEntities) {
3175 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
3178 const auto* pArchetype = world_entity_archetype(world, entity);
3179 const auto idx = core::get_index(scratch.archetypes, pArchetype);
3180 if (idx == BadIndex) {
3181 scratch.archetypes.push_back(pArchetype);
3182 scratch.bucketEntities.push_back(entity);
3183 scratch.counts.push_back(1);
3185 ++scratch.counts[idx];
3190 const auto archetypeCnt = (uint32_t)scratch.archetypes.size();
3191 GAIA_FOR(archetypeCnt) {
3192 if (match_direct_entity_terms(world, scratch.bucketEntities[i], queryInfo, seedInfo))
3193 cnt += scratch.counts[i];
3200 template <
typename TIter>
3202 auto& scratch = direct_query_scratch();
3203 const auto seenVersion = next_direct_query_seen_version(scratch);
3204 const bool hasDirectNotTerms = has_direct_not_terms(queryInfo);
3207 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3208 if (term.op != QueryOpKind::Or)
3211 (void)for_each_direct_term_entity(world, term, [&](
Entity entity) {
3212 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
3215 const auto entityId = (uint32_t)entity.id();
3216 ensure_direct_query_count_capacity(scratch, entityId);
3218 if (scratch.counts[entityId] == seenVersion)
3220 scratch.counts[entityId] = seenVersion;
3222 bool rejected =
false;
3223 if (hasDirectNotTerms) {
3224 for (
const auto& notTerm: queryInfo.ctx().data.terms_view()) {
3225 if (notTerm.op != QueryOpKind::Not)
3227 if (match_entity_term(world, entity, notTerm)) {
3248 template <
typename TIter>
3250 auto& scratch = direct_query_scratch();
3251 const auto seenVersion = next_direct_query_seen_version(scratch);
3252 const bool hasDirectNotTerms = has_direct_not_terms(queryInfo);
3254 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3255 if (term.op != QueryOpKind::Or)
3258 const bool completed = for_each_direct_term_entity(world, term, [&](
Entity entity) {
3259 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
3262 const auto entityId = (uint32_t)entity.id();
3263 ensure_direct_query_count_capacity(scratch, entityId);
3265 if (scratch.counts[entityId] == seenVersion)
3267 scratch.counts[entityId] = seenVersion;
3269 bool rejected =
false;
3270 if (hasDirectNotTerms) {
3271 for (
const auto& notTerm: queryInfo.ctx().data.terms_view()) {
3272 if (notTerm.op != QueryOpKind::Not)
3274 if (match_entity_term(world, entity, notTerm)) {
3294 static DirectEntitySeedInfo
3296 auto& scratch = direct_query_scratch();
3299 const auto plan = direct_entity_seed_plan(world, queryInfo);
3301 if (plan.hasAllTerms && !plan.preferOrSeed) {
3302 if (plan.bestAllTerm != EntityBad) {
3303 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3304 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
3306 if (term.op != QueryOpKind::All || term.id != plan.bestAllTerm ||
3307 term.matchKind != plan.bestAllTermMatchKind)
3309 collect_direct_term_entities(world, term, out);
3310 seedInfo.seededAllMatchKind = term.matchKind;
3313 seedInfo.seededFromAll =
true;
3314 seedInfo.seededAllTerm = plan.bestAllTerm;
3319 const auto seenVersion = next_direct_query_seen_version(scratch);
3321 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3322 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
3324 if (term.op != QueryOpKind::Or)
3327 scratch.termEntities.clear();
3328 collect_direct_term_entities(world, term, scratch.termEntities);
3329 for (
const auto entity: scratch.termEntities) {
3330 const auto entityId = (uint32_t)entity.id();
3331 ensure_direct_query_count_capacity(scratch, entityId);
3333 if (scratch.counts[entityId] == seenVersion)
3335 scratch.counts[entityId] = seenVersion;
3336 out.push_back(entity);
3340 seedInfo.seededFromOr =
true;
3348 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3349 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
3351 if (term.op == QueryOpKind::Not)
3364 template <
typename TIter,
typename Func>
3366 auto& scratch = direct_query_scratch();
3367 const auto seenVersion = next_direct_query_seen_version(scratch);
3369 seedInfo.seededFromOr =
true;
3371 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3372 if (term.op != QueryOpKind::Or)
3375 (void)for_each_direct_term_entity(world, term, [&](
Entity entity) {
3376 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
3379 const auto entityId = (uint32_t)entity.id();
3380 ensure_direct_query_count_capacity(scratch, entityId);
3382 if (scratch.counts[entityId] == seenVersion)
3384 scratch.counts[entityId] = seenVersion;
3386 if (!match_direct_entity_terms(world, entity, queryInfo, seedInfo))
3396 template <
bool UseFilters,
typename TIter>
3398 const bool hasRuntimeGroupFilter = queryInfo.ctx().data.
groupBy != EntityBad && m_groupIdSet != 0;
3400 if constexpr (!UseFilters) {
3401 if (!hasRuntimeGroupFilter && can_use_direct_entity_seed_eval(queryInfo)) {
3402 if (has_only_direct_or_terms(queryInfo))
3403 return is_empty_direct_or_union<TIter>(*queryInfo.world(), queryInfo);
3405 const auto plan = direct_entity_seed_plan(*queryInfo.world(), queryInfo);
3407 (void)for_each_direct_all_seed<TIter>(*queryInfo.world(), queryInfo, plan, [&](
Entity) {
3416 const auto cacheView = queryInfo.cache_archetype_view();
3417 const bool needsBarrierCache = has_depth_order_hierarchy_enabled_barrier(queryInfo);
3418 if (needsBarrierCache)
3419 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
3420 uint32_t idxFrom = 0;
3421 uint32_t idxTo = (uint32_t)cacheView.size();
3422 if (hasRuntimeGroupFilter) {
3424 if (pGroupData ==
nullptr)
3426 idxFrom = pGroupData->idxFirst;
3427 idxTo = pGroupData->idxLast + 1;
3430 for (uint32_t qi = idxFrom; qi < idxTo; ++qi) {
3431 const auto* pArchetype = cacheView[qi];
3432 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(qi);
3433 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
3436 GAIA_PROF_SCOPE(query::empty);
3438 const auto& chunks = pArchetype->chunks();
3439 if (!hasEntityFilters) {
3440 for (
auto* pChunk: chunks) {
3441 if (TIter::size(pChunk) == 0)
3443 if constexpr (UseFilters) {
3444 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
3453 it.set_world(queryInfo.world());
3454 it.set_archetype(pArchetype);
3456 const bool isNotEmpty = core::has_if(chunks, [&](
Chunk* pChunk) {
3457 it.set_chunk(pChunk);
3458 if constexpr (UseFilters)
3459 if (it.size() == 0 || !match_filters(*pChunk, queryInfo, m_changedWorldVersion))
3461 if (!hasEntityFilters)
3462 return it.size() > 0;
3464 const auto entities = it.template view<Entity>();
3465 const auto cnt = it.size();
3467 if (match_entity_filters(*queryInfo.world(), entities[i], queryInfo))
3482 bool hasOrTerms =
false;
3483 bool anyOrMatched =
false;
3486 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3487 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
3490 const auto id = term.id;
3491 const bool isDirectIsTerm = uses_non_direct_is_matching(term);
3492 const bool isInheritedTerm = uses_inherited_id_matching(world, term);
3493 const bool isAdjunctTerm =
3494 (
id.pair() && world_is_exclusive_dont_fragment_relation(world, entity_from_id(world,
id.
id()))) ||
3495 (!
id.
pair() && world_is_non_fragmenting_out_of_line_component(world,
id));
3496 const bool needsEntityFilter = isAdjunctTerm || isDirectIsTerm || isInheritedTerm ||
3497 (hasEntityFilterTerms && term.op == QueryOpKind::Or);
3498 if (!needsEntityFilter)
3501 const bool present = match_entity_term(world, entity, term);
3503 case QueryOpKind::All:
3507 case QueryOpKind::Or:
3509 anyOrMatched |= present;
3511 case QueryOpKind::Not:
3515 case QueryOpKind::Any:
3516 case QueryOpKind::Count:
3521 return !hasOrTerms || anyOrMatched;
3531 if (targetEntities.empty())
3534 const auto& world = *queryInfo.world();
3536 if (can_use_direct_target_eval(queryInfo)) {
3537 const auto directTargetEvalKind = queryInfo.direct_target_eval_kind();
3538 if (directTargetEvalKind != QueryCtx::DirectTargetEvalKind::Generic) {
3539 const auto termId = queryInfo.direct_target_eval_id();
3540 if (targetEntities.size() == 1) {
3541 const auto entity = targetEntities[0];
3542 if (!match_direct_entity_constraints<Iter>(world, queryInfo, entity))
3544 return match_single_direct_target_term(world, entity, termId, directTargetEvalKind);
3547 for (
const auto entity: targetEntities) {
3548 if (!match_direct_entity_constraints<Iter>(world, queryInfo, entity))
3550 if (match_single_direct_target_term(world, entity, termId, directTargetEvalKind))
3558 if (targetEntities.size() == 1) {
3559 const auto entity = targetEntities[0];
3560 if (!match_direct_entity_constraints<Iter>(world, queryInfo, entity))
3562 return match_direct_entity_terms(world, entity, queryInfo, seedInfo);
3565 for (
const auto entity: targetEntities) {
3566 if (!match_direct_entity_constraints<Iter>(world, queryInfo, entity))
3568 if (match_direct_entity_terms(world, entity, queryInfo, seedInfo))
3575 if (!match_one(queryInfo, archetype, targetEntities))
3581 for (
const auto entity: targetEntities) {
3582 if (!match_direct_entity_constraints<Iter>(world, queryInfo, entity))
3584 if (match_entity_filters(world, entity, queryInfo))
3592 template <
bool UseFilters,
typename TIter>
3594 const bool hasRuntimeGroupFilter = queryInfo.ctx().data.
groupBy != EntityBad && m_groupIdSet != 0;
3596 if constexpr (!UseFilters) {
3597 if (!hasRuntimeGroupFilter && can_use_direct_entity_seed_eval(queryInfo)) {
3598 auto& scratch = direct_query_scratch();
3599 if (has_only_direct_or_terms(queryInfo))
3600 return count_direct_or_union<TIter>(*queryInfo.world(), queryInfo);
3602 const auto plan = direct_entity_seed_plan(*queryInfo.world(), queryInfo);
3603 const auto seedInfo = build_direct_entity_seed(*queryInfo.world(), queryInfo, scratch.entities);
3605 if (can_use_archetype_bucket_count(*queryInfo.world(), queryInfo, seedInfo))
3606 return count_direct_entity_seed_by_archetype<TIter>(
3607 *queryInfo.world(), queryInfo, scratch.entities, seedInfo);
3610 (void)for_each_direct_all_seed<TIter>(*queryInfo.world(), queryInfo, plan, [&](
Entity) {
3621 const auto cacheView = queryInfo.cache_archetype_view();
3622 const bool needsBarrierCache = has_depth_order_hierarchy_enabled_barrier(queryInfo);
3623 if (needsBarrierCache)
3624 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
3626 uint32_t idxFrom = 0;
3627 uint32_t idxTo = (uint32_t)cacheView.size();
3628 if (hasRuntimeGroupFilter) {
3630 if (pGroupData ==
nullptr)
3633 idxFrom = pGroupData->idxFirst;
3634 idxTo = pGroupData->idxLast + 1;
3637 for (uint32_t qi = idxFrom; qi < idxTo; ++qi) {
3638 const auto* pArchetype = cacheView[qi];
3639 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(qi);
3640 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
3643 GAIA_PROF_SCOPE(query::count);
3645 const auto& chunks = pArchetype->chunks();
3646 if (!hasEntityFilters) {
3647 for (
auto* pChunk: chunks) {
3648 const auto entityCnt = TIter::size(pChunk);
3652 if constexpr (UseFilters) {
3653 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
3663 it.set_world(queryInfo.world());
3664 it.set_archetype(pArchetype);
3665 for (
auto* pChunk: chunks) {
3666 it.set_chunk(pChunk);
3668 const auto entityCnt = it.size();
3673 if constexpr (UseFilters) {
3674 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
3678 if (hasEntityFilters) {
3679 const auto entities = it.template view<Entity>();
3680 GAIA_FOR(entityCnt) {
3681 if (match_entity_filters(*queryInfo.world(), entities[i], queryInfo))
3695 template <
typename TIter>
3696 static void init_direct_entity_iter(
3700 GAIA_ASSERT(ec.
pChunk !=
nullptr);
3706 pTermIds[i] = EntityBad;
3709 const auto terms = queryInfo.ctx().data.terms_view();
3710 const auto queryIdCnt = (uint32_t)terms.size();
3712 GAIA_FOR(queryIdCnt) {
3713 const auto& term = terms[i];
3714 const auto fieldIdx = term.fieldIndex;
3715 const auto queryId = term.id;
3716 pTermIds[fieldIdx] = queryId;
3717 if (!indicesView.empty()) {
3718 pIndices[fieldIdx] = indicesView[fieldIdx];
3721 if (!queryId.pair() && world_is_out_of_line_component(world, queryId)) {
3722 const auto compIdx = core::get_index_unsafe(ec.
pArchetype->ids_view(), queryId);
3723 GAIA_ASSERT(compIdx != BadIndex);
3724 pIndices[fieldIdx] = 0xFF;
3728 auto compIdx = world_component_index_comp_idx(world, *ec.
pArchetype, queryId);
3729 if (compIdx == BadIndex)
3730 compIdx = core::get_index(ec.
pArchetype->ids_view(), queryId);
3731 pIndices[fieldIdx] = (uint8_t)compIdx;
3735 it.set_comp_indices(pIndices);
3736 const auto inheritedDataView = queryInfo.inherited_data_view(ec.
pArchetype);
3737 it.set_inherited_data(inheritedDataView);
3738 it.set_term_ids(pTermIds);
3742 it.set_chunk(ec.
pChunk, ec.
row, (uint16_t)(ec.
row + 1));
3746 template <
typename TIter>
3747 static void init_direct_entity_iter(
3748 const QueryInfo& queryInfo,
const World& world, Entity entity, TIter& it, uint8_t* pIndices,
3750 const auto& ec = ::gaia::ecs::fetch(world, entity);
3751 const Archetype* pLastArchetype =
nullptr;
3752 it.set_world(&world);
3753 init_direct_entity_iter(queryInfo, world, ec, it, pIndices, pTermIds, pLastArchetype);
3756 template <
typename TIter>
3758 init_direct_entity_iter_basic(
const EntityContainer& ec, TIter& it,
const Archetype*& pLastArchetype) {
3759 GAIA_ASSERT(ec.pArchetype !=
nullptr);
3760 GAIA_ASSERT(ec.pChunk !=
nullptr);
3761 GAIA_ASSERT(ec.row < ec.pChunk->size());
3763 if (ec.pArchetype != pLastArchetype) {
3764 it.set_archetype(ec.pArchetype);
3765 pLastArchetype = ec.pArchetype;
3767 it.set_chunk(ec.pChunk, ec.row, (uint16_t)(ec.row + 1));
3771 template <
typename TIter,
typename Func>
3773 auto& world = *queryInfo.world();
3775 it.set_world(&world);
3776 it.set_write_im(
false);
3777 const Archetype* pLastArchetype =
nullptr;
3781 for (
const auto& run: runs) {
3782 const auto& ec = ::gaia::ecs::fetch(world, run.pChunk->entity_view()[run.from]);
3783 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
3784 it.set_chunk(run.pChunk, run.from, run.to);
3787 finish_iter_writes(it);
3788 it.clear_touched_writes();
3792 template <
typename TIter,
typename Func,
typename... T>
3793 void each_chunk_runs(
3795 [[maybe_unused]] core::func_type_list<T...>) {
3796 auto& world = *queryInfo.world();
3797 const bool canUseBasicInit = (can_use_direct_bfs_chunk_term_eval<T>(world, queryInfo) && ...);
3798 if constexpr ((can_use_raw_chunk_row_arg<T>() && ...)) {
3799 if (canUseBasicInit) {
3800 for (
const auto& run: runs)
3801 run_query_on_chunk_rows_direct(run.pChunk, run.from, run.to, func, core::func_type_list<T...>{});
3806 if (canUseBasicInit) {
3808 it.set_world(&world);
3809 const Archetype* pLastArchetype =
nullptr;
3810 for (
const auto& run: runs) {
3811 if (run.pArchetype != pLastArchetype) {
3812 it.set_archetype(run.pArchetype);
3813 pLastArchetype = run.pArchetype;
3816 it.set_chunk(run.pChunk, run.from, run.to);
3818 run_query_on_chunk_direct(it, func, core::func_type_list<T...>{});
3824 it.set_world(&world);
3825 const Archetype* pLastArchetype =
nullptr;
3828 for (
const auto& run: runs) {
3829 const auto& ec = ::gaia::ecs::fetch(world, run.pChunk->entity_view()[run.from]);
3830 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
3831 it.set_chunk(run.pChunk, run.from, run.to);
3833 run_query_on_chunk_direct(it, func, core::func_type_list<T...>{});
3837 template <
typename TIter,
typename Func>
3839 auto& world = *queryInfo.world();
3840 auto& walkData = ensure_each_walk_data();
3842 it.set_world(&world);
3843 it.set_write_im(
false);
3844 if (!walkData.cachedRuns.empty()) {
3845 each_chunk_runs_iter<TIter>(queryInfo, walkData.cachedRuns, func);
3849 const Archetype* pLastArchetype =
nullptr;
3852 for (
const auto entity: entities) {
3853 const auto& ec = ::gaia::ecs::fetch(world, entity);
3854 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
3856 finish_iter_writes(it);
3857 it.clear_touched_writes();
3861 template <
typename T>
3862 GAIA_NODISCARD
static bool can_use_direct_bfs_chunk_term_eval(World& world,
const QueryInfo& queryInfo) {
3863 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
3864 if constexpr (std::is_same_v<Arg, Entity>)
3867 using FT =
typename component_type_t<Arg>::TypeFull;
3868 if constexpr (is_pair<FT>::value)
3870 const auto id = comp_cache(world).template get<FT>().entity;
3871 if (world_is_out_of_line_component(world,
id))
3873 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
3876 if (term.src != EntityBad || term.entTrav != EntityBad || term_has_variables(term))
3878 if (uses_non_direct_is_matching(term) || uses_inherited_id_matching(world, term) ||
3879 is_adjunct_direct_term(world, term))
3888 template <
typename... T>
3889 GAIA_NODISCARD
static bool can_use_direct_chunk_term_eval(World& world,
const QueryInfo& queryInfo) {
3890 if (queryInfo.has_entity_filter_terms())
3893 if constexpr (
sizeof...(T) == 0)
3896 return (can_use_direct_bfs_chunk_term_eval<T>(world, queryInfo) && ...);
3899 template <
typename T>
3900 GAIA_NODISCARD
static constexpr bool can_use_raw_chunk_row_arg() {
3901 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
3902 if constexpr (std::is_same_v<Arg, Entity>)
3905 return !std::is_lvalue_reference_v<T> || std::is_const_v<std::remove_reference_t<T>>;
3908 template <
typename TIter,
typename Func,
typename... T>
3909 void each_direct_entities(
3911 [[maybe_unused]] core::func_type_list<T...>) {
3912 auto& world = *queryInfo.world();
3913 const bool canUseBasicInit = (can_use_direct_bfs_chunk_term_eval<T>(world, queryInfo) && ...);
3914 auto& walkData = ensure_each_walk_data();
3915 if (!walkData.cachedRuns.empty()) {
3916 each_chunk_runs<TIter>(queryInfo, walkData.cachedRuns, func, core::func_type_list<T...>{});
3921 it.set_world(&world);
3922 const Archetype* pLastArchetype =
nullptr;
3925 for (
const auto entity: entities) {
3926 const auto& ec = ::gaia::ecs::fetch(world, entity);
3927 if (canUseBasicInit)
3928 init_direct_entity_iter_basic(ec, it, pLastArchetype);
3930 init_direct_entity_iter(queryInfo, world, ec, it, indices, termIds, pLastArchetype);
3932 if (canUseBasicInit)
3933 run_query_on_direct_entity_direct(it, func, core::func_type_list<T...>{});
3935 run_query_on_direct_entity(it, func, core::func_type_list<T...>{});
3939 template <
typename T>
3940 static Entity inherited_query_arg_id(World& world) {
3941 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
3942 if constexpr (std::is_same_v<Arg, Entity>)
3945 using FT =
typename component_type_t<Arg>::TypeFull;
3946 if constexpr (is_pair<FT>::value) {
3947 const auto rel = comp_cache(world).template get<typename FT::rel>().entity;
3948 const auto tgt = comp_cache(world).template get<typename FT::tgt>().entity;
3949 return (Entity)Pair(rel, tgt);
3951 return comp_cache(world).template get<FT>().entity;
3955 template <
typename T>
3956 static decltype(
auto) inherited_query_entity_arg_by_id(World& world, Entity entity, Entity termId) {
3957 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
3958 if constexpr (std::is_same_v<Arg, Entity>)
3960 else if constexpr (std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>)
3961 return world_query_entity_arg_by_id_raw<T>(world, entity, termId);
3963 return world_query_entity_arg_by_id<T>(world, entity, termId);
3966 template <
typename T>
3967 static decltype(
auto) inherited_query_entity_arg_by_id_cached(
3968 World& world, Entity entity, Entity termId,
const Archetype*& pLastArchetype, Entity& cachedOwner,
3969 bool& cachedDirect) {
3970 using Arg = std::remove_cv_t<std::remove_reference_t<T>>;
3971 if constexpr (std::is_same_v<Arg, Entity>)
3973 else if constexpr (std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>)
3974 return inherited_query_entity_arg_by_id<T>(world, entity, termId);
3976 return world_query_entity_arg_by_id_cached_const<T>(
3977 world, entity, termId, pLastArchetype, cachedOwner, cachedDirect);
3980 template <
typename... T,
typename Func,
size_t... I>
3981 static void invoke_inherited_query_args_by_id(
3982 World& world, Entity entity,
const Entity* pArgIds, Func& func, std::index_sequence<I...>) {
3983 func(inherited_query_entity_arg_by_id<T>(world, entity, pArgIds[I])...);
3986 template <
typename... T,
typename Func,
size_t... I>
3987 static void invoke_inherited_query_args_by_id_cached(
3988 World& world, Entity entity,
const Entity* pArgIds,
const Archetype** pLastArchetypes,
3989 Entity* pCachedOwners,
bool* pCachedDirect, Func& func, std::index_sequence<I...>) {
3991 inherited_query_entity_arg_by_id_cached<T>(
3992 world, entity, pArgIds[I], pLastArchetypes[I], pCachedOwners[I], pCachedDirect[I])...);
3995 template <
typename... T,
typename Func,
size_t... I>
3996 static void invoke_query_args_by_id(
3997 World& world, Entity entity,
const Entity* pArgIds, Func& func, std::index_sequence<I...>) {
3998 func(world_query_entity_arg_by_id<T>(world, entity, pArgIds[I])...);
4001 template <
typename... T,
size_t... I>
4003 finish_query_args_by_id(World& world, Entity entity,
const Entity* pArgIds, std::index_sequence<I...>) {
4004 Entity seenTerms[
sizeof...(T) > 0 ?
sizeof...(T) : 1]{};
4005 uint32_t seenCnt = 0;
4006 const auto finish_term = [&](Entity term) {
4008 if (seenTerms[i] == term)
4011 seenTerms[seenCnt++] = term;
4012 world_finish_write(world, term, entity);
4017 if constexpr (is_write_query_arg<T>())
4018 finish_term(pArgIds[I]);
4024 template <
typename TIter,
typename Func>
4026 auto& world = *queryInfo.world();
4027 const bool hasWriteTerms = queryInfo.ctx().data.
readWriteMask != 0;
4028 const auto plan = direct_entity_seed_plan(world, queryInfo);
4030 auto exec_entity = [&](
Entity entity) {
4034 init_direct_entity_iter(queryInfo, world, entity, it, indices, termIds);
4035 it.set_write_im(
false);
4037 finish_iter_writes(it);
4040 if (hasWriteTerms) {
4041 auto& scratch = direct_query_scratch();
4044 const auto seedInfo = build_direct_entity_seed(world, queryInfo, scratch.entities);
4045 for (
const auto entity: scratch.entities) {
4046 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
4048 if (!match_direct_entity_terms(world, entity, queryInfo, seedInfo))
4050 exec_entity(entity);
4055 if (!plan.preferOrSeed) {
4056 const auto* pSeedTerm = find_direct_all_seed_term(queryInfo, plan);
4057 if (pSeedTerm !=
nullptr && can_use_direct_seed_run_cache(world, queryInfo, *pSeedTerm)) {
4059 seedInfo.seededAllTerm = pSeedTerm->id;
4060 seedInfo.seededAllMatchKind = pSeedTerm->matchKind;
4061 seedInfo.seededFromAll =
true;
4062 each_chunk_runs_iter<TIter>(
4063 queryInfo, cached_direct_seed_runs<TIter>(queryInfo, *pSeedTerm, seedInfo), func);
4068 if (plan.preferOrSeed) {
4069 for_each_direct_or_union<TIter>(world, queryInfo, exec_entity);
4073 (void)for_each_direct_all_seed<TIter>(world, queryInfo, plan, [&](
Entity entity) {
4074 exec_entity(entity);
4080 template <
typename TIter,
typename Func,
typename... T>
4082 constexpr bool needsInheritedArgIds =
4083 (!std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
Entity> || ... ||
false);
4085 auto& world = *queryInfo.world();
4086 const auto plan = direct_entity_seed_plan(world, queryInfo);
4087 const bool hasWriteTerms = queryInfo.ctx().data.
readWriteMask != 0;
4088 bool hasInheritedTerms =
false;
4089 for (
const auto& term: queryInfo.ctx().data.terms_view()) {
4090 if (uses_inherited_id_matching(world, term)) {
4091 hasInheritedTerms =
true;
4096 if (!hasWriteTerms && !plan.preferOrSeed) {
4097 const auto* pSeedTerm = find_direct_all_seed_term(queryInfo, plan);
4098 if (pSeedTerm !=
nullptr && can_use_direct_seed_run_cache(world, queryInfo, *pSeedTerm)) {
4100 seedInfo.seededAllTerm = pSeedTerm->id;
4101 seedInfo.seededAllMatchKind = pSeedTerm->matchKind;
4102 seedInfo.seededFromAll =
true;
4103 if (!hasInheritedTerms) {
4104 each_chunk_runs<TIter>(
4105 queryInfo, cached_direct_seed_runs<TIter>(queryInfo, *pSeedTerm, seedInfo), func,
4108 const auto entities = cached_direct_seed_chunk_entities<TIter>(queryInfo, *pSeedTerm, seedInfo);
4109 Entity inheritedArgIds[
sizeof...(T) > 0 ?
sizeof...(T) : 1] = {inherited_query_arg_id<T>(world)...};
4110 const Archetype* lastArchetypes[
sizeof...(T) > 0 ?
sizeof...(T) : 1]{};
4111 Entity cachedOwners[
sizeof...(T) > 0 ?
sizeof...(T) : 1]{};
4112 bool cachedDirect[
sizeof...(T) > 0 ?
sizeof...(T) : 1]{};
4113 for (
const auto entity: entities) {
4114 invoke_inherited_query_args_by_id_cached<T...>(
4115 world, entity, inheritedArgIds, lastArchetypes, cachedOwners, cachedDirect, func,
4116 std::index_sequence_for<T...>{});
4123 auto exec_direct_entity = [&](
Entity entity) {
4127 init_direct_entity_iter(queryInfo, world, entity, it, indices, termIds);
4131 auto walk_entities = [&](
auto&& exec_entity) {
4132 if (hasWriteTerms) {
4133 auto& scratch = direct_query_scratch();
4136 const auto seedInfo = build_direct_entity_seed(world, queryInfo, scratch.entities);
4137 for (
const auto entity: scratch.entities) {
4138 if (!match_direct_entity_constraints<TIter>(world, queryInfo, entity))
4140 if (!match_direct_entity_terms(world, entity, queryInfo, seedInfo))
4142 exec_entity(entity);
4147 if (plan.preferOrSeed) {
4148 for_each_direct_or_union<TIter>(world, queryInfo, [&](
Entity entity) {
4149 exec_entity(entity);
4155 (void)for_each_direct_all_seed<TIter>(world, queryInfo, plan, [&](
Entity entity) {
4156 exec_entity(entity);
4161 if constexpr (!needsInheritedArgIds)
4162 walk_entities(exec_direct_entity);
4164 Entity inheritedArgIds[
sizeof...(T) > 0 ?
sizeof...(T) : 1] = {inherited_query_arg_id<T>(world)...};
4165 auto exec_entity = [&](
Entity entity) {
4166 if (hasInheritedTerms) {
4167 invoke_inherited_query_args_by_id<T...>(
4168 world, entity, inheritedArgIds, func, std::index_sequence_for<T...>{});
4169 finish_query_args_by_id<T...>(world, entity, inheritedArgIds, std::index_sequence_for<T...>{});
4173 exec_direct_entity(entity);
4176 walk_entities(exec_entity);
4180 template <
bool UseFilters,
typename TIter,
typename ContainerOut>
4181 void arr_inter(
QueryInfo& queryInfo, ContainerOut& outArray) {
4182 using ContainerItemType =
typename ContainerOut::value_type;
4183 if constexpr (!UseFilters) {
4184 if (can_use_direct_entity_seed_eval(queryInfo)) {
4185 auto& world = *queryInfo.world();
4186 const auto plan = direct_entity_seed_plan(world, queryInfo);
4187 if (plan.preferOrSeed) {
4188 for_each_direct_or_union<TIter>(world, queryInfo, [&](
Entity entity) {
4189 if constexpr (std::is_same_v<ContainerItemType, Entity>)
4190 outArray.push_back(entity);
4192 auto tmp = world_direct_entity_arg<ContainerItemType>(world, entity);
4193 outArray.push_back(tmp);
4199 (void)for_each_direct_all_seed<TIter>(world, queryInfo, plan, [&](Entity entity) {
4200 if constexpr (std::is_same_v<ContainerItemType, Entity>)
4201 outArray.push_back(entity);
4203 auto tmp = world_direct_entity_arg<ContainerItemType>(world, entity);
4204 outArray.push_back(tmp);
4213 auto& world = *
const_cast<World*
>(queryInfo.world());
4214 if constexpr (!UseFilters) {
4216 can_use_direct_chunk_term_eval<ContainerItemType>(world, queryInfo) &&
4217 can_use_direct_chunk_iteration_fastpath(queryInfo)) {
4218 const auto cacheView = queryInfo.cache_archetype_view();
4219 uint32_t idxFrom = 0;
4220 uint32_t idxTo = (uint32_t)cacheView.size();
4221 if (queryInfo.ctx().data.
groupBy != EntityBad && m_groupIdSet != 0) {
4223 if (pGroupData ==
nullptr)
4225 idxFrom = pGroupData->idxFirst;
4226 idxTo = pGroupData->idxLast + 1;
4229 for (uint32_t i = idxFrom; i < idxTo; ++i) {
4230 auto* pArchetype = cacheView[i];
4231 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype))
4234 GAIA_PROF_SCOPE(query::arr);
4236 const auto& chunks = pArchetype->chunks();
4237 for (
auto* pChunk: chunks) {
4238 const auto from = TIter::start_index(pChunk);
4239 const auto to = TIter::end_index(pChunk);
4240 if GAIA_UNLIKELY (from == to)
4243 const auto dataView = chunk_view_auto<ContainerItemType>(pChunk);
4244 for (uint16_t row = from; row < to; ++row)
4245 outArray.push_back(dataView[row]);
4254 it.set_world(queryInfo.world());
4256 const auto cacheView = queryInfo.cache_archetype_view();
4257 const auto sortView = queryInfo.cache_sort_view();
4258 const bool needsBarrierCache = has_depth_order_hierarchy_enabled_barrier(queryInfo);
4259 if (needsBarrierCache)
4260 const_cast<QueryInfo&
>(queryInfo).ensure_depth_order_hierarchy_barrier_cache();
4261 uint32_t idxFrom = 0;
4262 uint32_t idxTo = (uint32_t)cacheView.size();
4263 if (queryInfo.ctx().data.
groupBy != EntityBad && m_groupIdSet != 0) {
4265 if (pGroupData ==
nullptr)
4267 idxFrom = pGroupData->idxFirst;
4268 idxTo = pGroupData->idxLast + 1;
4271 const auto append_rows = [&](
const uint32_t archetypeIdx,
auto* pArchetype,
auto* pChunk, uint16_t from,
4273 const bool barrierPasses = !needsBarrierCache || queryInfo.barrier_passes(archetypeIdx);
4274 if GAIA_UNLIKELY (!can_process_archetype_inter<TIter>(queryInfo, *pArchetype, barrierPasses))
4277 GAIA_PROF_SCOPE(query::arr);
4279 it.set_archetype(pArchetype);
4280 it.set_chunk(pChunk, from, to);
4282 const auto cnt = it.size();
4286 if constexpr (UseFilters) {
4287 if (!match_filters(*pChunk, queryInfo, m_changedWorldVersion))
4291 const auto dataView = it.template view<ContainerItemType>();
4292 if (!hasEntityFilters) {
4294 const auto idx = it.template acc_index<ContainerItemType>(i);
4295 auto tmp = dataView[idx];
4296 outArray.push_back(tmp);
4299 const auto entities = it.template view<Entity>();
4301 if (!match_entity_filters(*queryInfo.world(), entities[i], queryInfo))
4303 const auto idx = it.template acc_index<ContainerItemType>(i);
4304 auto tmp = dataView[idx];
4305 outArray.push_back(tmp);
4310 if (!sortView.empty()) {
4311 for (
const auto& view: sortView) {
4312 if (view.archetypeIdx < idxFrom || view.archetypeIdx >= idxTo)
4315 const auto minStartRow = TIter::start_index(view.pChunk);
4316 const auto minEndRow = TIter::end_index(view.pChunk);
4317 const auto viewFrom = view.startRow;
4318 const auto viewTo = (uint16_t)(view.startRow + view.count);
4319 const auto startRow = core::get_max(minStartRow, viewFrom);
4320 const auto endRow = core::get_min(minEndRow, viewTo);
4321 if (startRow == endRow)
4325 view.archetypeIdx,
const_cast<Archetype*
>(cacheView[view.archetypeIdx]), view.pChunk, startRow,
4331 for (uint32_t i = idxFrom; i < idxTo; ++i) {
4332 auto* pArchetype = cacheView[i];
4333 const auto& chunks = pArchetype->chunks();
4334 for (
auto* pChunk: chunks)
4335 append_rows(i, pArchetype, pChunk, 0, 0);
4340 QueryImpl() =
default;
4341 ~QueryImpl() =
default;
4344 World& world, QueryCache& queryCache, ArchetypeId& nextArchetypeId, uint32_t& worldVersion,
4345 const EntityToArchetypeMap& entityToArchetypeMap,
const ArchetypeDArray& allArchetypes):
4346 m_nextArchetypeId(&nextArchetypeId), m_worldVersion(&worldVersion),
4347 m_entityToArchetypeMap(&entityToArchetypeMap), m_allArchetypes(&allArchetypes) {
4348 m_storage.
init(&world, &queryCache);
4353 GAIA_NODISCARD QueryId
id()
const {
4354 if (!uses_query_cache_storage())
4361 GAIA_NODISCARD uint32_t
gen()
const {
4362 if (!uses_query_cache_storage())
4372 m_eachWalkData.reset();
4373 m_directSeedRunData.reset();
4374 reset_changed_filter_state();
4375 invalidate_each_walk_cache();
4376 invalidate_direct_seed_run_cache();
4382 m_eachWalkData.reset();
4383 m_directSeedRunData.reset();
4384 reset_changed_filter_state();
4385 invalidate_each_walk_cache();
4386 invalidate_direct_seed_run_cache();
4392 return uses_query_cache_storage() && m_storage.
is_cached();
4439 GAIA_ASSERT(str !=
nullptr);
4444 va_start(args, str);
4448 uint32_t parentDepth = 0;
4451 uint32_t varNamesCnt = 0;
4453 auto expr = util::trim(exprRaw);
4454 return expr.size() == 5 && expr[0] ==
'$' && expr[1] ==
't' && expr[2] ==
'h' && expr[3] ==
'i' &&
4459 auto varNameSpan = util::trim(varExpr);
4460 if (varNameSpan.empty())
4464 if (is_reserved_var_name(varName)) {
4465 GAIA_ASSERT2(
false,
"$this is reserved and can only be used as a source expression: Id($this)");
4469 const auto namedVar = find_var_by_name(varName);
4470 if (namedVar != EntityBad)
4473 GAIA_FOR(varNamesCnt) {
4474 if (varNames[i].size() != varName.size())
4476 if (varNames[i].size() > 0 && memcmp(varNames[i].data(), varName.data(), varName.size()) != 0)
4478 return query_var_entity(i);
4481 if (varNamesCnt >= varNames.size()) {
4482 GAIA_ASSERT2(
false,
"Too many query variables in expression");
4486 const auto idx = varNamesCnt++;
4487 varNames[idx] = varName;
4489 const auto varEntity = query_var_entity(idx);
4490 (void)set_var_name_internal(varEntity, varName);
4495 auto expr = util::trim(exprRaw);
4500 return find_or_alloc_var(expr.subspan(1));
4502 if (expr[0] ==
'(') {
4503 if (expr.back() !=
')') {
4504 GAIA_ASSERT2(
false,
"Expression '(' not terminated");
4508 const auto idStr = expr.subspan(1, expr.size() - 2);
4509 const auto commaIdx = core::get_index(idStr,
',');
4510 if (commaIdx == BadIndex) {
4511 GAIA_ASSERT2(
false,
"Pair expression does not contain ','");
4515 const auto first = self(self, idStr.subspan(0, commaIdx));
4516 if (first == EntityBad)
4518 const auto second = self(self, idStr.subspan(commaIdx + 1));
4519 if (second == EntityBad)
4525 return expr_to_entity((
const World&)*m_storage.
world(), args, expr);
4529 auto srcExpr = util::trim(srcExprRaw);
4530 if (srcExpr.empty())
4534 if (is_this_expr(srcExpr)) {
4539 srcOut = parse_entity_expr(parse_entity_expr, srcExpr);
4540 return srcOut != EntityBad;
4544 auto expr = util::trim(exprRaw);
4548 if (expr.back() ==
')') {
4550 int32_t openIdx = -1;
4551 for (int32_t i = (int32_t)expr.size() - 1; i >= 0; --i) {
4552 if (expr[(uint32_t)i] ==
')')
4554 else if (expr[(uint32_t)i] ==
'(') {
4565 auto idExpr = util::trim(expr.subspan(0, (uint32_t)openIdx));
4566 auto srcExpr = util::trim(expr.subspan((uint32_t)openIdx + 1, expr.size() - (uint32_t)openIdx - 2));
4567 if (!idExpr.empty() && !srcExpr.empty()) {
4568 id = parse_entity_expr(parse_entity_expr, idExpr);
4569 if (
id == EntityBad)
4573 if (!parse_src_expr(srcExpr, src))
4582 id = parse_entity_expr(parse_entity_expr, expr);
4583 return id != EntityBad;
4587 auto expr = util::trim(exprRaw);
4591 bool isReadWrite =
false;
4592 if (!expr.empty() && expr[0] ==
'&') {
4594 expr = util::trim(expr.subspan(1));
4601 Entity entity = EntityBad;
4602 if (!parse_term_expr(expr, entity, options))
4606 case QueryOpKind::All:
4607 all(entity, options);
4609 case QueryOpKind::Or:
4610 or_(entity, options);
4612 case QueryOpKind::Not:
4613 no(entity, options);
4615 case QueryOpKind::Any:
4616 any(entity, options);
4626 auto process = [&]() {
4630 auto expr = util::trim(exprRaw);
4635 bool hasOrChain =
false;
4638 const auto cnt = (uint32_t)expr.size();
4639 for (uint32_t i = 0; i + 1 < cnt; ++i) {
4640 const auto ch = expr[i];
4643 else if (ch ==
')') {
4644 GAIA_ASSERT(depth > 0);
4646 }
else if (depth == 0 && ch ==
'|' && expr[i + 1] ==
'|') {
4655 uint32_t partBeg = 0;
4656 const auto cnt = (uint32_t)expr.size();
4657 for (uint32_t i = 0; i < cnt; ++i) {
4658 const auto ch = expr[i];
4661 else if (ch ==
')') {
4662 GAIA_ASSERT(depth > 0);
4666 const bool isOr = i + 1 < cnt && depth == 0 && ch ==
'|' && expr[i + 1] ==
'|';
4667 const bool isEnd = i + 1 == cnt;
4668 if (!isOr && !isEnd)
4671 const auto partEnd = isOr ? i : (i + 1);
4672 auto partExpr = expr.subspan(partBeg, partEnd - partBeg);
4673 if (!add_term(QueryOpKind::Or, partExpr))
4685 QueryOpKind op = QueryOpKind::All;
4686 if (expr[0] ==
'?') {
4687 op = QueryOpKind::Any;
4688 expr = util::trim(expr.subspan(1));
4689 }
else if (expr[0] ==
'!') {
4690 op = QueryOpKind::Not;
4691 expr = util::trim(expr.subspan(1));
4694 return add_term(op, expr);
4697 for (; str[pos] != 0; ++pos) {
4698 if (str[pos] ==
'(')
4700 else if (str[pos] ==
')') {
4701 GAIA_ASSERT(parentDepth > 0);
4703 }
else if (str[pos] ==
',' && parentDepth == 0) {
4731 return all(
Pair(Is, entity), options);
4742 return all(
Pair(Is, entity), options);
4752 add_entity_term(QueryOpKind::All, entity, options);
4760 template <
typename T>
4762 add_inter<T>(QueryOpKind::All, options);
4769 template <
typename T>
4772 add_inter<T>(QueryOpKind::All);
4783 add_entity_term(QueryOpKind::Any, entity, options);
4791 template <
typename T>
4793 add_inter<T>(QueryOpKind::Any, options);
4800 template <
typename T>
4803 add_inter<T>(QueryOpKind::Any);
4815 add_entity_term(QueryOpKind::Or, entity, options);
4823 template <
typename T>
4825 add_inter<T>(QueryOpKind::Or, options);
4832 template <
typename T>
4834 add_inter<T>(QueryOpKind::Or);
4845 add_entity_term(QueryOpKind::Not, entity, options);
4853 template <
typename T>
4855 add_inter<T>(QueryOpKind::Not, options);
4862 template <
typename T>
4865 add_inter<T>(QueryOpKind::Not);
4875 [[maybe_unused]]
const bool ok = set_var_name_internal(varEntity, name);
4881 GAIA_ASSERT(name !=
nullptr);
4882 if (name ==
nullptr)
4884 return var_name(varEntity,
util::str_view{name, (uint32_t)GAIA_STRLEN(name, 256)});
4892 const bool ok = is_query_var_entity(varEntity);
4897 const auto idx = query_var_idx(varEntity);
4898 m_varBindings[idx] = value;
4899 m_varBindingsMask |= (uint8_t(1) << idx);
4906 const auto varEntity = find_var_by_name(name);
4907 GAIA_ASSERT(varEntity != EntityBad);
4908 if (varEntity == EntityBad)
4910 return set_var(varEntity, value);
4914 GAIA_ASSERT(name !=
nullptr);
4915 if (name ==
nullptr)
4917 return set_var(
util::str_view{name, (uint32_t)GAIA_STRLEN(name, 256)}, value);
4923 const bool ok = is_query_var_entity(varEntity);
4928 const auto idx = query_var_idx(varEntity);
4929 m_varBindingsMask &= (uint8_t)~(uint8_t(1) << idx);
4934 m_varBindingsMask = 0;
4944 changed_inter(entity);
4951 template <
typename T>
4966 sort_by_inter(entity, func);
4974 template <
typename T>
4976 sort_by_inter<T>(func);
4985 template <
typename Rel,
typename Tgt>
4987 sort_by_inter<Rel, Tgt>(func);
4995 Entity m_relation = EntityBad;
5000 template <
typename Func>
5001 void each(Func func) {
5002 m_query->each_walk(func, m_relation);
5025 GAIA_ASSERT(!relation.pair());
5026 GAIA_ASSERT(world_supports_depth_order(*m_storage.
world(), relation));
5027 group_by_inter(relation, group_by_func_depth_order);
5033 template <
typename Rel>
5035 using UO =
typename component_type_t<Rel>::TypeOriginal;
5036 static_assert(core::is_raw_v<UO>,
"Use depth_order() with raw relation types only");
5038 const auto& desc = comp_cache_add<Rel>(*m_storage.
world());
5039 return depth_order(desc.entity);
5048 group_by_inter(entity, func);
5055 template <
typename T>
5057 group_by_inter<T>(func);
5065 template <
typename Rel,
typename Tgt>
5067 group_by_inter<Rel, Tgt>(func);
5077 group_dep_inter(relation);
5084 template <
typename Rel>
5086 group_dep_inter<Rel>();
5095 set_group_id_inter(groupId);
5102 GAIA_ASSERT(!entity.pair());
5103 set_group_id_inter(entity.id());
5109 template <
typename T>
5111 set_group_id_inter<T>();
5119 template <
typename Func>
5121 each_inter<QueryExecType::Default, Func>(func);
5127 template <
typename Func>
5128 void each(Func func, QueryExecType execType) {
5130 case QueryExecType::Parallel:
5131 each_inter<QueryExecType::Parallel, Func>(func);
5133 case QueryExecType::ParallelPerf:
5134 each_inter<QueryExecType::ParallelPerf, Func>(func);
5136 case QueryExecType::ParallelEff:
5137 each_inter<QueryExecType::ParallelEff, Func>(func);
5140 each_inter<QueryExecType::Default, Func>(func);
5149 template <
typename Func>
5151 auto& queryInfo = fetch();
5152 match_all(queryInfo);
5154 if constexpr (std::is_invocable_v<Func, IterAll&>) {
5155 run_query_on_archetypes<QueryExecType::Default, IterAll>(queryInfo, [&](
IterAll& it) {
5156 GAIA_PROF_SCOPE(query_func_a);
5159 }
else if constexpr (std::is_invocable_v<Func, Iter&>) {
5160 run_query_on_archetypes<QueryExecType::Default, Iter>(queryInfo, [&](
Iter& it) {
5161 GAIA_PROF_SCOPE(query_func_a);
5164 }
else if constexpr (std::is_invocable_v<Func, IterDisabled&>) {
5165 run_query_on_archetypes<QueryExecType::Default, IterDisabled>(queryInfo, [&](
IterDisabled& it) {
5166 GAIA_PROF_SCOPE(query_func_a);
5182 bool empty(Constraints constraints = Constraints::EnabledOnly) {
5183 auto& queryInfo = fetch();
5184 if (!queryInfo.has_filters() && m_groupIdSet == 0 && can_use_direct_entity_seed_eval(queryInfo)) {
5185 switch (constraints) {
5186 case Constraints::EnabledOnly:
5187 return empty_inter<false, Iter>(queryInfo);
5188 case Constraints::DisabledOnly:
5189 return empty_inter<false, IterDisabled>(queryInfo);
5190 case Constraints::AcceptAll:
5191 return empty_inter<false, IterAll>(queryInfo);
5195 match_all(queryInfo);
5197 const bool hasFilters = queryInfo.has_filters();
5199 switch (constraints) {
5200 case Constraints::EnabledOnly:
5201 return empty_inter<true, Iter>(queryInfo);
5202 case Constraints::DisabledOnly:
5203 return empty_inter<true, IterDisabled>(queryInfo);
5204 case Constraints::AcceptAll:
5205 return empty_inter<true, IterAll>(queryInfo);
5208 switch (constraints) {
5209 case Constraints::EnabledOnly:
5210 return empty_inter<false, Iter>(queryInfo);
5211 case Constraints::DisabledOnly:
5212 return empty_inter<false, IterDisabled>(queryInfo);
5213 case Constraints::AcceptAll:
5214 return empty_inter<false, IterAll>(queryInfo);
5228 uint32_t
count(Constraints constraints = Constraints::EnabledOnly) {
5229 auto& queryInfo = fetch();
5230 if (!queryInfo.has_filters() && m_groupIdSet == 0 && can_use_direct_entity_seed_eval(queryInfo)) {
5231 switch (constraints) {
5232 case Constraints::EnabledOnly:
5233 return count_inter<false, Iter>(queryInfo);
5234 case Constraints::DisabledOnly:
5235 return count_inter<false, IterDisabled>(queryInfo);
5236 case Constraints::AcceptAll:
5237 return count_inter<false, IterAll>(queryInfo);
5241 match_all(queryInfo);
5243 uint32_t entCnt = 0;
5245 const bool hasFilters = queryInfo.has_filters();
5247 switch (constraints) {
5248 case Constraints::EnabledOnly: {
5249 entCnt += count_inter<true, Iter>(queryInfo);
5251 case Constraints::DisabledOnly: {
5252 entCnt += count_inter<true, IterDisabled>(queryInfo);
5254 case Constraints::AcceptAll: {
5255 entCnt += count_inter<true, IterAll>(queryInfo);
5259 switch (constraints) {
5260 case Constraints::EnabledOnly: {
5261 entCnt += count_inter<false, Iter>(queryInfo);
5263 case Constraints::DisabledOnly: {
5264 entCnt += count_inter<false, IterDisabled>(queryInfo);
5266 case Constraints::AcceptAll: {
5267 entCnt += count_inter<false, IterAll>(queryInfo);
5279 template <
typename Container>
5280 void arr(Container& outArray, Constraints constraints = Constraints::EnabledOnly) {
5281 const auto entCnt = count(constraints);
5285 outArray.reserve(entCnt);
5286 auto& queryInfo = fetch();
5287 match_all(queryInfo);
5289 const bool hasFilters = queryInfo.has_filters();
5291 switch (constraints) {
5292 case Constraints::EnabledOnly:
5293 arr_inter<true, Iter>(queryInfo, outArray);
5295 case Constraints::DisabledOnly:
5296 arr_inter<true, IterDisabled>(queryInfo, outArray);
5298 case Constraints::AcceptAll:
5299 arr_inter<true, IterAll>(queryInfo, outArray);
5303 switch (constraints) {
5304 case Constraints::EnabledOnly:
5305 arr_inter<false, Iter>(queryInfo, outArray);
5307 case Constraints::DisabledOnly:
5308 arr_inter<false, IterDisabled>(queryInfo, outArray);
5310 case Constraints::AcceptAll:
5311 arr_inter<false, IterAll>(queryInfo, outArray);
5323 QueryInfo& queryInfo,
Entity relation, Constraints constraints = Constraints::EnabledOnly) {
5324 struct OrderedWalkTargetCtx {
5327 uint32_t dependentIdx = 0;
5332 uint32_t* pEdgeCnt =
nullptr;
5334 GAIA_NODISCARD
static uint32_t
5336 const auto targetId = entity.id();
5338 uint32_t high = cnt;
5339 while (low < high) {
5340 const uint32_t mid = low + ((high - low) >> 1);
5341 if (entities[mid].
id() < targetId)
5347 if (low < cnt && entities[low].
id() == targetId)
5352 static void count_edge(
void* rawCtx,
Entity dependency) {
5353 auto& ctx = *
static_cast<OrderedWalkTargetCtx*
>(rawCtx);
5354 const auto dependencyIdx = find_entity_idx(*ctx.pEntities, ctx.cnt, dependency);
5355 if (dependencyIdx == ctx.cnt || dependencyIdx == ctx.dependentIdx)
5358 ++(*ctx.pOutdegree)[dependencyIdx];
5359 ++(*ctx.pIndegree)[ctx.dependentIdx];
5363 static void write_edge(
void* rawCtx,
Entity dependency) {
5364 auto& ctx = *
static_cast<OrderedWalkTargetCtx*
>(rawCtx);
5365 const auto dependencyIdx = find_entity_idx(*ctx.pEntities, ctx.cnt, dependency);
5366 if (dependencyIdx == ctx.cnt || dependencyIdx == ctx.dependentIdx)
5369 (*ctx.pEdges)[(*ctx.pWriteCursor)[dependencyIdx]++] = ctx.dependentIdx;
5373 auto& walkData = ensure_each_walk_data();
5374 auto& world = *m_storage.
world();
5375 const uint32_t relationVersion = world_rel_version(world, relation);
5376 const uint32_t worldVersion = ::gaia::ecs::world_version(world);
5378 const bool needsTraversalBarrierState =
5379 constraints == Constraints::EnabledOnly && ::gaia::ecs::valid(world, relation);
5380 auto survives_disabled_barrier = [&](
Entity entity) {
5381 if (!needsTraversalBarrierState)
5385 GAIA_FOR(MAX_TRAV_DEPTH) {
5386 const auto next = target(world, curr, relation);
5387 if (next == EntityBad || next == curr)
5389 if (!world_entity_enabled(world, next))
5397 if (walkData.cacheValid && walkData.cachedRelation == relation && walkData.cachedConstraints == constraints &&
5398 walkData.cachedRelationVersion == relationVersion &&
5399 (!needsTraversalBarrierState || walkData.cachedEntityVersion == worldVersion) &&
5400 !queryInfo.has_filters()) {
5401 auto& chunks = walkData.scratchChunks;
5404 bool chunkChanged =
false;
5405 for (
auto* pArchetype: queryInfo) {
5406 if (pArchetype ==
nullptr || !can_process_archetype(queryInfo, *pArchetype))
5409 for (
const auto* pChunk: pArchetype->chunks()) {
5410 if (pChunk ==
nullptr)
5413 chunks.push_back(pChunk);
5414 if (!chunkChanged && pChunk->changed(walkData.cachedEntityVersion))
5415 chunkChanged =
true;
5419 bool sameChunks = chunks.size() == walkData.cachedChunks.size();
5421 for (uint32_t i = 0; i < (uint32_t)chunks.size(); ++i) {
5422 if (chunks[i] != walkData.cachedChunks[i]) {
5429 if (sameChunks && !chunkChanged) {
5434 auto& entities = walkData.scratchEntities;
5436 arr(entities, constraints);
5437 if (entities.empty())
5440 if (needsTraversalBarrierState) {
5441 uint32_t writeIdx = 0;
5442 const auto cnt = (uint32_t)entities.size();
5444 const auto entity = entities[i];
5445 if (!survives_disabled_barrier(entity))
5447 entities[writeIdx++] = entity;
5449 entities.resize(writeIdx);
5450 if (entities.empty())
5454 if (walkData.cacheValid && walkData.cachedRelation == relation && walkData.cachedConstraints == constraints &&
5455 walkData.cachedRelationVersion == relationVersion &&
5456 (!needsTraversalBarrierState || walkData.cachedEntityVersion == worldVersion) &&
5457 entities.size() == walkData.cachedInput.size()) {
5458 bool sameInput =
true;
5459 for (uint32_t i = 0; i < (uint32_t)entities.size(); ++i) {
5460 if (entities[i] != walkData.cachedInput[i]) {
5471 auto& ordered = walkData.cachedOutput;
5472 walkData.cachedInput = entities;
5474 if (!::gaia::ecs::valid(world, relation)) {
5476 return left.id() < right.id();
5482 return left.id() < right.id();
5485 const auto cnt = (uint32_t)entities.size();
5487 auto& indegree = walkData.scratchIndegree;
5488 indegree.resize(cnt);
5489 auto& outdegree = walkData.scratchOutdegree;
5490 outdegree.resize(cnt);
5491 for (uint32_t i = 0; i < cnt; ++i) {
5496 uint32_t edgeCnt = 0;
5497 OrderedWalkTargetCtx edgeCtx;
5498 edgeCtx.pEntities = &entities;
5500 edgeCtx.pIndegree = &indegree;
5501 edgeCtx.pOutdegree = &outdegree;
5502 edgeCtx.pEdgeCnt = &edgeCnt;
5503 for (uint32_t dependentIdx = 0; dependentIdx < cnt; ++dependentIdx) {
5504 const auto dependent = entities[dependentIdx];
5505 edgeCtx.dependentIdx = dependentIdx;
5506 world_for_each_target(world, dependent, relation, &edgeCtx, &OrderedWalkTargetCtx::count_edge);
5509 auto& offsets = walkData.scratchOffsets;
5510 offsets.resize(cnt + 1);
5512 for (uint32_t i = 0; i < cnt; ++i)
5513 offsets[i + 1] = offsets[i] + outdegree[i];
5515 auto& writeCursor = walkData.scratchWriteCursor;
5516 writeCursor.resize(cnt);
5517 for (uint32_t i = 0; i < cnt; ++i)
5518 writeCursor[i] = offsets[i];
5520 auto& edges = walkData.scratchEdges;
5521 edges.resize(edgeCnt);
5522 edgeCtx.pWriteCursor = &writeCursor;
5523 edgeCtx.pEdges = &edges;
5524 for (uint32_t dependentIdx = 0; dependentIdx < cnt; ++dependentIdx) {
5525 const auto dependent = entities[dependentIdx];
5526 edgeCtx.dependentIdx = dependentIdx;
5527 world_for_each_target(world, dependent, relation, &edgeCtx, &OrderedWalkTargetCtx::write_edge);
5530 ordered.reserve(cnt);
5532 auto& currLevel = walkData.scratchCurrLevel;
5534 auto& nextLevel = walkData.scratchNextLevel;
5536 for (uint32_t i = 0; i < cnt; ++i) {
5537 if (indegree[i] == 0)
5538 currLevel.push_back(i);
5542 core::sort(level, [&](uint32_t left, uint32_t right) {
5543 return entities[left].id() < entities[right].
id();
5547 sort_level(currLevel);
5549 uint32_t executedCnt = 0;
5550 while (!currLevel.empty()) {
5551 for (
auto idx: currLevel) {
5552 ordered.push_back(entities[idx]);
5557 for (
auto idx: currLevel) {
5558 for (uint32_t edgePos = offsets[idx]; edgePos < offsets[idx + 1]; ++edgePos) {
5559 const auto dependentIdx = edges[edgePos];
5560 if (indegree[dependentIdx] == 0)
5563 --indegree[dependentIdx];
5564 if (indegree[dependentIdx] == 0)
5565 nextLevel.push_back(dependentIdx);
5569 if (nextLevel.empty())
5572 sort_level(nextLevel);
5574 currLevel.reserve(nextLevel.size());
5575 for (
auto idx: nextLevel)
5576 currLevel.push_back(idx);
5580 if (executedCnt != cnt) {
5581 for (uint32_t i = 0; i < cnt; ++i) {
5582 if (indegree[i] > 0)
5583 ordered.push_back(entities[i]);
5588 walkData.cachedRelation = relation;
5589 walkData.cachedConstraints = constraints;
5590 walkData.cachedRelationVersion = relationVersion;
5591 walkData.cachedEntityVersion = ::gaia::ecs::world_version(world);
5592 walkData.cachedRuns.clear();
5595 const auto orderedCnt = (uint32_t)ordered.size();
5596 if (orderedCnt != 0) {
5597 for (uint32_t i = 0; i < orderedCnt; ++i) {
5598 const auto& ec = ::gaia::ecs::fetch(world, ordered[i]);
5599 if (walkData.cachedRuns.empty()) {
5600 walkData.cachedRuns.push_back({ec.pArchetype, ec.pChunk, ec.row, (uint16_t)(ec.row + 1), i});
5604 auto& run = walkData.cachedRuns.back();
5605 if (ec.pChunk == run.pChunk && ec.row == run.to) {
5606 run.to = (uint16_t)(run.to + 1);
5608 walkData.cachedRuns.push_back({ec.pArchetype, ec.pChunk, ec.row, (uint16_t)(ec.row + 1), i});
5614 if (!queryInfo.has_filters()) {
5615 auto& chunks = walkData.scratchChunks;
5617 for (
auto* pArchetype: queryInfo) {
5618 if (pArchetype ==
nullptr || !can_process_archetype(queryInfo, *pArchetype))
5621 for (
const auto* pChunk: pArchetype->chunks()) {
5622 if (pChunk ==
nullptr)
5624 chunks.push_back(pChunk);
5627 walkData.cachedChunks = chunks;
5629 walkData.cachedChunks.clear();
5630 walkData.cacheValid =
true;
5642 template <
typename Func>
5643 void each_walk(Func func,
Entity relation, Constraints constraints = Constraints::EnabledOnly) {
5644 auto& queryInfo = fetch();
5645 match_all(queryInfo);
5646 const auto ordered = ordered_entities_walk(queryInfo, relation, constraints);
5648 if constexpr (std::is_invocable_v<Func, IterAll&>) {
5649 each_direct_entities_iter<IterAll>(queryInfo, ordered, func);
5650 }
else if constexpr (std::is_invocable_v<Func, Iter&>) {
5651 each_direct_entities_iter<Iter>(queryInfo, ordered, func);
5652 }
else if constexpr (std::is_invocable_v<Func, IterDisabled&>) {
5653 each_direct_entities_iter<IterDisabled>(queryInfo, ordered, func);
5654 }
else if constexpr (std::is_invocable_v<Func, const Entity&> || std::is_invocable_v<Func, Entity>) {
5655 for (
const auto entity: ordered)
5658 using InputArgs =
decltype(core::func_args(&Func::operator()));
5659 GAIA_ASSERT(unpack_args_into_query_has_all(queryInfo, InputArgs{}));
5660 GAIA_ASSERT(can_use_direct_target_eval(queryInfo));
5661 if (!can_use_direct_target_eval(queryInfo))
5664 switch (constraints) {
5665 case Constraints::EnabledOnly:
5666 each_direct_entities<Iter>(queryInfo, ordered, func, InputArgs{});
5668 case Constraints::DisabledOnly:
5669 each_direct_entities<IterDisabled>(queryInfo, ordered, func, InputArgs{});
5671 case Constraints::AcceptAll:
5672 each_direct_entities<IterAll>(queryInfo, ordered, func, InputArgs{});
5683 auto& queryInfo = fetch();
5684 match_all(queryInfo);
5685 if (uses_shared_cache_layer())
5686 GAIA_LOG_N(
"BEG DIAG Query %u.%u [S]",
id(), gen());
5687 else if (uses_query_cache_storage())
5688 GAIA_LOG_N(
"BEG DIAG Query %u.%u [L]",
id(), gen());
5690 GAIA_LOG_N(
"BEG DIAG Query [U]");
5691 for (
const auto* pArchetype: queryInfo)
5692 Archetype::diag_basic_info(*m_storage.
world(), *pArchetype);
5693 GAIA_LOG_N(
"END DIAG Query");
5699 auto& queryInfo = fetch();
5700 return queryInfo.bytecode();
5705 const auto dump = bytecode();
5706 if (uses_shared_cache_layer())
5707 GAIA_LOG_N(
"BEG DIAG Query Bytecode %u.%u [S]",
id(), gen());
5708 else if (uses_query_cache_storage())
5709 GAIA_LOG_N(
"BEG DIAG Query Bytecode %u.%u [L]",
id(), gen());
5711 GAIA_LOG_N(
"BEG DIAG Query Bytecode [U]");
5712 GAIA_LOG_N(
"%.*s", (
int)dump.size(), dump.data());
5713 GAIA_LOG_N(
"END DIAG Query");