2#include "gaia/config/config.h"
7#include "gaia/cnt/darray.h"
8#include "gaia/cnt/set.h"
9#include "gaia/config/profiler.h"
10#include "gaia/core/utility.h"
11#include "gaia/ecs/api.h"
12#include "gaia/ecs/archetype.h"
13#include "gaia/ecs/archetype_common.h"
14#include "gaia/ecs/id.h"
15#include "gaia/ecs/query_common.h"
16#include "gaia/ecs/query_mask.h"
17#include "gaia/ecs/ser_binary.h"
21 using EntityToArchetypeMap = cnt::map<EntityLookupKey, ArchetypeDArray>;
25 enum class MatchingStyle { Simple, Wildcard, Complex };
71 GAIA_ASSERT(key != EntityBadLookupKey);
73 if (ent ==
Pair(All, All))
76 const auto it = map.find(key);
77 if (it == map.end() || it->second.empty())
85 GAIA_ASSERT(src != EntityBad);
87 return fetch_archetypes_for_select(map, arr, ent,
EntityLookupKey(src));
91 enum class EOpcode : uint8_t {
105 using VmLabel = uint16_t;
126 inline uint32_t handle_last_archetype_match(
128 const auto cache_it = pCont->find(entityKey);
129 uint32_t lastMatchedIdx = 0;
130 if (cache_it == pCont->end())
131 pCont->emplace(entityKey, pSrcArchetype->size());
133 lastMatchedIdx = cache_it->second;
134 cache_it->second = pSrcArchetype->size();
136 return lastMatchedIdx;
141 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
142 return match_entity_mask(maskArchetype, maskQuery);
144 static void restart([[maybe_unused]] uint32_t& idx) {}
145 static bool can_continue(
bool hasMatch) {
148 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
149 return expectedMatches == totalMatches;
158 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
159 return match_entity_mask(maskArchetype, maskQuery);
161 static void restart([[maybe_unused]] uint32_t& idx) {}
162 static bool can_continue(
bool hasMatch) {
165 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
166 (void)expectedMatches;
167 return totalMatches > 0;
176 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
177 return !match_entity_mask(maskArchetype, maskQuery);
179 static void restart(uint32_t& idx) {
182 static bool can_continue(
bool hasMatch) {
185 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
186 (void)expectedMatches;
187 return totalMatches == 0;
195 template <
typename OpKind>
196 inline bool match_inter_eval_matches(uint32_t queryIdMarches, uint32_t& outMatches) {
197 const bool hadAnyMatches = queryIdMarches > 0;
201 if (!OpKind::can_continue(hadAnyMatches))
206 outMatches += (uint32_t)hadAnyMatches;
218 template <
typename OpKind,
typename CmpFunc>
219 GAIA_NODISCARD
inline bool match_inter(
EntitySpan queryIds,
EntitySpan archetypeIds, CmpFunc func) {
220 const auto archetypeIdsCnt = (uint32_t)archetypeIds.size();
221 const auto queryIdsCnt = (uint32_t)queryIds.size();
224 uint32_t indices[2]{};
225 uint32_t matches = 0;
252 while (indices[0] < queryIdsCnt) {
253 const auto idInQuery = queryIds[indices[0]];
256 if (idInQuery == All || idInQuery.id() == Is.id())
259 uint32_t queryIdMatches = 0;
260 while (indices[1] < archetypeIdsCnt) {
261 const auto idInArchetype = archetypeIds[indices[1]];
264 const auto res = func(idInQuery, idInArchetype);
274 if (!match_inter_eval_matches<OpKind>(queryIdMatches, matches))
283 if (!match_inter_eval_matches<OpKind>(queryIdMatches, matches))
290 OpKind::restart(indices[1]);
296 return OpKind::eval(queryIdsCnt, matches);
304 return {idInQuery == idInArchetype};
307 GAIA_NODISCARD
inline IdCmpResult cmp_ids_pairs(
Entity idInQuery,
Entity idInArchetype) {
308 if (idInQuery.pair()) {
310 if (idInQuery ==
Pair(All, All))
318 if (idInQuery.gen() == All.id())
319 return {idInQuery.id() == idInArchetype.id()};
326 if (idInQuery.id() == All.id())
327 return {idInQuery.gen() == idInArchetype.gen()};
331 return cmp_ids(idInQuery, idInArchetype);
334 GAIA_NODISCARD
inline IdCmpResult
335 cmp_ids_is(
const World& w,
const Archetype& archetype, Entity idInQuery, Entity idInArchetype) {
337 if (idInQuery.pair() && idInQuery.id() == Is.id()) {
338 auto archetypeIds = archetype.ids_view();
340 idInQuery.gen() == idInArchetype.id() ||
341 as_relations_trav_if(w, idInQuery, [&](Entity relation) {
342 const auto idx = core::get_index(archetypeIds, relation);
344 return idx != BadIndex;
349 return cmp_ids(idInQuery, idInArchetype);
352 GAIA_NODISCARD
inline IdCmpResult
353 cmp_ids_is_pairs(
const World& w,
const Archetype& archetype, Entity idInQuery, Entity idInArchetype) {
354 if (idInQuery.pair()) {
356 if (idInQuery == Pair(All, All))
360 if (idInQuery.id() == Is.id()) {
362 if (idInArchetype == idInQuery)
365 auto archetypeIds = archetype.ids_view();
366 const auto eQ = entity_from_id(w, idInQuery.gen());
367 if (eQ == idInArchetype)
372 if (idInArchetype.id() == Is.id()) {
373 const auto eA = entity_from_id(w, idInArchetype.gen());
377 return {as_relations_trav_if(w, eQ, [eA](Entity relation) {
378 return eA == relation;
383 return {as_relations_trav_if(w, eQ, [&archetypeIds](Entity relation) {
386 const auto idx = core::get_index(archetypeIds, relation);
388 return idx != BadIndex;
397 if (idInQuery.id() == All.id()) {
398 if (idInQuery.gen() == idInArchetype.gen())
402 if (archetype.pairs_is() > 0) {
403 auto archetypeIds = archetype.ids_view();
405 const auto e = entity_from_id(w, idInQuery.gen());
406 return {as_relations_trav_if(w, e, [&](Entity relation) {
409 const auto idx = core::get_index(archetypeIds, relation);
411 return idx != BadIndex;
424 if (idInQuery.gen() == All.id()) {
425 return {idInQuery.id() == idInArchetype.id()};
430 return cmp_ids(idInQuery, idInArchetype);
439 template <
typename OpKind>
440 GAIA_NODISCARD
inline bool match_res(
const Archetype& archetype, EntitySpan queryIds) {
443 if (archetype.pairs() == 0) {
444 return match_inter<OpKind>(
445 queryIds, archetype.ids_view(),
447 [](Entity idInQuery, Entity idInArchetype) {
448 return cmp_ids(idInQuery, idInArchetype);
453 return match_inter<OpKind>(
454 queryIds, archetype.ids_view(),
456 [](Entity idInQuery, Entity idInArchetype) {
457 return cmp_ids_pairs(idInQuery, idInArchetype);
467 template <
typename OpKind>
468 GAIA_NODISCARD
inline bool match_res_as(
const World& w,
const Archetype& archetype, EntitySpan queryIds) {
470 if (archetype.pairs() == 0) {
471 return match_inter<OpKind>(
472 queryIds, archetype.ids_view(),
474 [&](Entity idInQuery, Entity idInArchetype) {
475 return cmp_ids_is(w, archetype, idInQuery, idInArchetype);
479 return match_inter<OpKind>(
480 queryIds, archetype.ids_view(),
482 [&](Entity idInQuery, Entity idInArchetype) {
483 return cmp_ids_is_pairs(w, archetype, idInQuery, idInArchetype);
487 template <
typename OpKind, MatchingStyle Style>
489 match_archetype_inter(MatchingCtx& ctx, EntityLookupKey entityKey,
const ArchetypeDArray* pSrcArchetypes) {
490 const auto& archetypes = *pSrcArchetypes;
491 auto& matchesArr = *ctx.pMatchesArr;
492 auto& matchesSet = *ctx.pMatchesSet;
494 uint32_t lastMatchedIdx = OpKind::handle_last_match(ctx, entityKey, pSrcArchetypes);
495 if (lastMatchedIdx >= archetypes.size())
498 auto archetypeSpan =
std::span(&archetypes[lastMatchedIdx], archetypes.size() - lastMatchedIdx);
500 if constexpr (Style == MatchingStyle::Complex) {
501 for (
auto* pArchetype: archetypeSpan) {
502 if (matchesSet.contains(pArchetype))
505 if (!match_res_as<OpKind>(*ctx.pWorld, *pArchetype, ctx.idsToMatch))
508 matchesSet.emplace(pArchetype);
509 matchesArr.emplace_back(pArchetype);
512#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
513 else if constexpr (Style == MatchingStyle::Simple) {
514 for (
auto* pArchetype: archetypeSpan) {
515 if (matchesSet.contains(pArchetype))
519 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
522 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
525 matchesSet.emplace(pArchetype);
526 matchesArr.emplace_back(pArchetype);
531 for (
auto* pArchetype: archetypeSpan) {
532 if (matchesSet.contains(pArchetype))
535 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
538 matchesSet.emplace(pArchetype);
539 matchesArr.emplace_back(pArchetype);
544 template <MatchingStyle Style>
545 inline void match_archetype_all(MatchingCtx& ctx) {
546 if constexpr (Style == MatchingStyle::Complex) {
549 const auto& allArchetypes = *ctx.pAllArchetypes;
551 if (ctx.ent.id() == Is.id()) {
553 const auto* pArchetypes = &allArchetypes;
555 match_archetype_inter<OpAll, Style>(ctx, EntityBadLookupKey, pArchetypes);
557 auto entityKey = EntityLookupKey(ctx.ent);
559 const auto* pArchetypes =
560 fetch_archetypes_for_select(*ctx.pEntityToArchetypeMap, *ctx.pAllArchetypes, ctx.ent, entityKey);
561 if (pArchetypes ==
nullptr)
564 match_archetype_inter<OpAll, Style>(ctx, entityKey, pArchetypes);
567 auto entityKey = EntityLookupKey(ctx.ent);
571 const auto* pArchetypes =
572 fetch_archetypes_for_select(*ctx.pEntityToArchetypeMap, *ctx.pAllArchetypes, ctx.ent, entityKey);
573 if (pArchetypes ==
nullptr)
576 match_archetype_inter<OpAll, Style>(ctx, entityKey, pArchetypes);
580 inline void match_archetype_one(MatchingCtx& ctx) {
581 EntityLookupKey entityKey(ctx.ent);
586 const auto* pArchetypes =
587 fetch_archetypes_for_select(*ctx.pEntityToArchetypeMap, *ctx.pAllArchetypes, ctx.ent, entityKey);
588 if (pArchetypes ==
nullptr)
592 match_archetype_inter<OpAny, MatchingStyle::Wildcard>(ctx, entityKey, pArchetypes);
595 inline void match_archetype_one_as(MatchingCtx& ctx) {
596 const auto& allArchetypes = *ctx.pAllArchetypes;
602 const ArchetypeDArray* pSrcArchetypes =
nullptr;
604 EntityLookupKey entityKey = EntityBadLookupKey;
606 if (ctx.ent.id() == Is.id()) {
608 pSrcArchetypes = &allArchetypes;
610 entityKey = EntityLookupKey(ctx.ent);
613 fetch_archetypes_for_select(*ctx.pEntityToArchetypeMap, *ctx.pAllArchetypes, ctx.ent, entityKey);
614 if (pSrcArchetypes ==
nullptr)
618 match_archetype_inter<OpAny, MatchingStyle::Complex>(ctx, entityKey, pSrcArchetypes);
621 template <MatchingStyle Style>
622 inline void match_archetype_no_2(MatchingCtx& ctx) {
626 if constexpr (Style == MatchingStyle::Complex) {
627 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
628 auto* pArchetype = (*ctx.pMatchesArr)[i];
630 if (match_res_as<OpNo>(*ctx.pWorld, *pArchetype, ctx.idsToMatch)) {
635 core::swap_erase(*ctx.pMatchesArr, i);
638#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
639 else if constexpr (Style == MatchingStyle::Simple) {
640 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
641 auto* pArchetype = (*ctx.pMatchesArr)[i];
644 if (OpNo::check_mask(pArchetype->queryMask(), ctx.queryMask))
647 if (match_res<OpNo>(*pArchetype, ctx.idsToMatch)) {
652 core::swap_erase(*ctx.pMatchesArr, i);
657 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
658 auto* pArchetype = (*ctx.pMatchesArr)[i];
660 if (match_res<OpNo>(*pArchetype, ctx.idsToMatch)) {
665 core::swap_erase(*ctx.pMatchesArr, i);
671 static constexpr EOpcode Id = EOpcode::All_Simple;
674 GAIA_PROF_SCOPE(vm::op_and_simple);
679 match_archetype_all<MatchingStyle::Simple>(ctx);
687 static constexpr EOpcode Id = EOpcode::All_Wildcard;
690 GAIA_PROF_SCOPE(vm::op_and_wildcard);
695 match_archetype_all<MatchingStyle::Wildcard>(ctx);
703 static constexpr EOpcode Id = EOpcode::All_Complex;
706 GAIA_PROF_SCOPE(vm::op_and_complex);
711 match_archetype_all<MatchingStyle::Complex>(ctx);
719 static constexpr EOpcode Id = EOpcode::Any_NoAll;
722 GAIA_PROF_SCOPE(vm::op_any);
724 const auto cnt = comp.
ids_any.size();
733 match_archetype_one(ctx);
738 match_archetype_one_as(ctx);
747 static constexpr EOpcode Id = EOpcode::Any_WithAll;
750 GAIA_PROF_SCOPE(vm::op_any);
755#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
756 const bool isSimple = (ctx.
flags & QueryCtx::QueryFlags::Complex) == 0U;
762 if (hasNoAsRelationships) {
763#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
765 for (uint32_t i = 0; i < ctx.
pMatchesArr->size();) {
768 GAIA_FOR_((uint32_t)comp.
ids_any.size(), j) {
770 if (!OpAny::check_mask(pArchetype->queryMask(), ctx.
queryMask))
771 goto checkNextArchetype3;
774 if (match_res<OpAny>(*pArchetype, ctx.
idsToMatch))
775 goto checkNextArchetype3;
788 for (uint32_t i = 0; i < ctx.
pMatchesArr->size();) {
791 GAIA_FOR_((uint32_t)comp.
ids_any.size(), j) {
793 if (match_res<OpAny>(*pArchetype, ctx.
idsToMatch))
794 goto checkNextArchetype1;
799 goto checkNextArchetype1;
811 for (uint32_t i = 0; i < ctx.
pMatchesArr->size();) {
814 GAIA_FOR_((uint32_t)comp.
ids_any.size(), j) {
818 goto checkNextArchetype2;
835 static constexpr EOpcode Id = EOpcode::Not_Simple;
838 GAIA_PROF_SCOPE(vm::op_not);
847 match_archetype_inter<detail::OpNo, MatchingStyle::Simple>(ctx, EntityBadLookupKey, ctx.
pAllArchetypes);
849 match_archetype_no_2<MatchingStyle::Simple>(ctx);
857 static constexpr EOpcode Id = EOpcode::Not_Wildcard;
860 GAIA_PROF_SCOPE(vm::op_not);
869 match_archetype_inter<detail::OpNo, MatchingStyle::Wildcard>(ctx, EntityBadLookupKey, ctx.
pAllArchetypes);
871 match_archetype_no_2<MatchingStyle::Wildcard>(ctx);
879 static constexpr EOpcode Id = EOpcode::Not_Complex;
882 GAIA_PROF_SCOPE(vm::op_not);
891 match_archetype_inter<detail::OpNo, MatchingStyle::Complex>(ctx, EntityBadLookupKey, ctx.
pAllArchetypes);
893 match_archetype_no_2<MatchingStyle::Complex>(ctx);
907 static constexpr OpcodeFunc OpcodeFuncs[] = {
911 return op.exec(comp, ctx);
916 return op.exec(comp, ctx);
921 return op.exec(comp, ctx);
926 return op.exec(comp, ctx);
931 return op.exec(comp, ctx);
936 return op.exec(comp, ctx);
941 return op.exec(comp, ctx);
946 return op.exec(comp, ctx);
954 const auto cnt = (detail::VmLabel)m_compCtx.ops.size();
956 op.pc_fail = cnt - 1;
957 m_compCtx.ops.push_back(GAIA_MOV(op));
966 GAIA_PROF_SCOPE(vm::compile);
968 auto& data = queryCtx.data;
970 QueryTermSpan terms = data.terms_view_mut();
971 QueryTermSpan terms_all = terms.subspan(0, data.firstAny);
972 QueryTermSpan terms_any = terms.subspan(data.firstAny, data.firstNot - data.firstAny);
973 QueryTermSpan terms_not = terms.subspan(data.firstNot);
976 if (!terms_all.empty()) {
977 GAIA_PROF_SCOPE(vm::compile_all);
979 const auto cnt = terms_all.size();
981 auto& p = terms_all[i];
982 if (p.src == EntityBad) {
983 m_compCtx.
ids_all.push_back(p.id);
989 p.srcArchetype = archetype_from_entity(*queryCtx.w, p.src);
992 if (p.srcArchetype ==
nullptr) {
993 m_compCtx.ops.clear();
1001 if (!terms_any.empty()) {
1002 GAIA_PROF_SCOPE(vm::compile_any);
1004 uint32_t archetypesWithId = 0;
1006 const auto cnt = terms_any.size();
1008 auto& p = terms_any[i];
1009 if (p.src != EntityBad) {
1010 p.srcArchetype = archetype_from_entity(*queryCtx.w, p.src);
1011 if (p.srcArchetype ==
nullptr)
1017 const auto* pArchetypes =
1018 fetch_archetypes_for_select(entityToArchetypeMap, allArchetypes, p.id,
EntityLookupKey(p.id));
1019 if (pArchetypes ==
nullptr)
1024 m_compCtx.
ids_any.push_back(p.id);
1028 if (archetypesWithId == 0) {
1029 m_compCtx.ops.clear();
1035 if (!terms_not.empty()) {
1036 GAIA_PROF_SCOPE(vm::compile_not);
1038 const auto cnt = terms_not.size();
1040 auto& p = terms_not[i];
1041 if (p.src != EntityBad)
1044 m_compCtx.
ids_not.push_back(p.id);
1048 create_opcodes(queryCtx);
1051 void create_opcodes(
QueryCtx& queryCtx) {
1052 const bool isSimple = (queryCtx.data.
flags & QueryCtx::QueryFlags::Complex) == 0U;
1056 m_compCtx.ops.clear();
1059 if (!m_compCtx.
ids_all.empty()) {
1062 op.
opcode = detail::EOpcode::All_Simple;
1064 op.opcode = detail::EOpcode::All_Complex;
1066 op.opcode = detail::EOpcode::All_Wildcard;
1067 (void)add_op(GAIA_MOV(op));
1071 if (!m_compCtx.
ids_any.empty()) {
1072 detail::CompiledOp op{};
1073 op.opcode = m_compCtx.
ids_all.empty() ? detail::EOpcode::Any_NoAll : detail::EOpcode::Any_WithAll;
1074 (void)add_op(GAIA_MOV(op));
1078 if (!m_compCtx.
ids_not.empty()) {
1079 detail::CompiledOp op{};
1081 op.opcode = detail::EOpcode::Not_Simple;
1083 op.opcode = detail::EOpcode::Not_Complex;
1085 op.opcode = detail::EOpcode::Not_Wildcard;
1086 (void)add_op(GAIA_MOV(op));
1090 queryCtx.data.
flags &= ~QueryCtx::QueryFlags::Recompile;
1093 GAIA_NODISCARD
bool is_compiled()
const {
1094 return !m_compCtx.ops.empty();
1099 GAIA_PROF_SCOPE(vm::exec);
1105 auto& stackItem = m_compCtx.ops[ctx.
pc];
1106 const bool ret = OpcodeFuncs[(uint32_t)stackItem.opcode](m_compCtx, ctx);
1107 ctx.
pc = ret ? stackItem.pc_ok : stackItem.pc_fail;
1108 }
while (ctx.
pc < m_compCtx.ops.size());
Array with variable size of elements of type.
Definition darray_impl.h:25
Array of elements of type.
Definition sarray_ext_impl.h:27
Definition span_impl.h:99
Wrapper for two Entities forming a relationship pair.
Definition id.h:395
void exec(MatchingCtx &ctx)
Executes compiled opcodes.
Definition vm.h:1098
void compile(const EntityToArchetypeMap &entityToArchetypeMap, const ArchetypeDArray &allArchetypes, QueryCtx &queryCtx)
Transforms inputs into virtual machine opcodes.
Definition vm.h:963
Definition robin_hood.h:720
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Hashmap lookup structure used for Entity.
Definition id.h:336
uint32_t as_mask_0
Mask for items with Is relationship pair. If the id is a pair, the first part (id) is written here.
Definition query_common.h:248
uint32_t as_mask_1
Mask for items with Is relationship pair. If the id is a pair, the second part (gen) is written here.
Definition query_common.h:251
uint8_t flags
Query flags.
Definition query_common.h:260
Definition query_common.h:197
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_Not
Idx of the last matched archetype against the NOT opcode.
Definition vm.h:46
cnt::set< Archetype * > * pMatchesSet
Set of already matched archetypes. Reset before each exec().
Definition vm.h:38
ArchetypeDArray * pMatchesArr
Array of already matches archetypes. Reset before each exec().
Definition vm.h:40
EntitySpan idsToMatch
List of entity ids in a query to consider.
Definition vm.h:64
Entity ent
Entity to match.
Definition vm.h:62
const ArchetypeDArray * pAllArchetypes
Array of all archetypes in the world.
Definition vm.h:36
uint8_t flags
Flags copied over from QueryCtx::Data.
Definition vm.h:56
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_All
Idx of the last matched archetype against the ALL opcode.
Definition vm.h:42
uint32_t pc
Current stack position (program counter)
Definition vm.h:66
QueryMask queryMask
Mask for speeding up simple query matching.
Definition vm.h:48
uint32_t as_mask_0
Mask for items with Is relationship pair. If the id is a pair, the first part (id) is written here.
Definition vm.h:51
const World * pWorld
World.
Definition vm.h:32
const EntityToArchetypeMap * pEntityToArchetypeMap
entity -> archetypes mapping
Definition vm.h:34
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_Any
Idx of the last matched archetype against the ANY opcode.
Definition vm.h:44
uint32_t as_mask_1
Mask for items with Is relationship pair. If the id is a pair, the second part (gen) is written here.
Definition vm.h:54
VmLabel pc_ok
Stack position to go to if the opcode returns true.
Definition vm.h:111
VmLabel pc_fail
Stack position to go to if the opcode returns false.
Definition vm.h:113
EOpcode opcode
Opcode to execute.
Definition vm.h:109
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_all
Array of ops that can be evaluated with a ALL opcode.
Definition vm.h:119
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_any
Array of ops that can be evaluated with a ANY opcode.
Definition vm.h:121
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_not
Array of ops that can be evaluated with a NOT opcode.
Definition vm.h:123