88 static constexpr uint16_t ARCHETYPE_LIFESPAN_BITS = 7;
90 static_assert(ARCHETYPE_LIFESPAN_BITS >= ChunkHeader::CHUNK_LIFESPAN_BITS);
92 static constexpr uint16_t MAX_ARCHETYPE_LIFESPAN = (1 << ARCHETYPE_LIFESPAN_BITS) - 1;
106 GAIA_NODISCARD
static Component empty_comp() noexcept {
107 return Component(IdentifierIdBad, 0, 0, 0, DataStorageType::Table);
111 return pItem ==
nullptr ? empty_comp() : pItem->comp;
115 ArchetypeIdLookupKey::LookupHash archetypeIdHash;
117 LookupHash hashLookup = {0};
119 QueryMask queryMask{};
120 Properties properties{};
122 ChunkDataOffsets dataOffsets{};
124 Entity ids[ChunkHeader::MAX_COMPONENTS];
126 const ComponentCacheItem* compItems[ChunkHeader::MAX_COMPONENTS]{};
128 ChunkDataOffset compOffs[ChunkHeader::MAX_COMPONENTS];
133 cnt::darray<Chunk*> chunks;
135 uint32_t firstFreeChunkIdx = 0;
142 uint8_t observedTermCnt = 0;
145 uint32_t deleteReq : 1;
149 uint32_t lifespanCountdownMax : ARCHETYPE_LIFESPAN_BITS;
151 uint32_t lifespanCountdown : ARCHETYPE_LIFESPAN_BITS;
153 RuntimeData(): deleteReq(0), dead(0), lifespanCountdownMax(1), lifespanCountdown(0) {}
158 ArchetypeGraph graph;
161 struct PairIndexData {
163 EntityId
id = IdentifierIdBad;
169 uint8_t pairIndexBuffer[ChunkHeader::MAX_COMPONENTS];
171 uint8_t pairsAsIndexBuffer[ChunkHeader::MAX_COMPONENTS];
172 uint8_t pairRelIndexBuffer[ChunkHeader::MAX_COMPONENTS];
173 uint8_t pairTgtIndexBuffer[ChunkHeader::MAX_COMPONENTS];
180 uint8_t pairCntIs = 0;
182 uint8_t pairRelCountCnt = 0;
184 uint8_t pairTgtCountCnt = 0;
186 static PairIndexData* create(
EntitySpan ids) {
187 auto* pPairIndex = mem::AllocHelper::alloc<PairIndexData>(
"ArchetypePairIndex");
188 (void)
new (pPairIndex) PairIndexData();
190 uint8_t pairRelIndexCnt = 0;
191 uint8_t pairTgtIndexCnt = 0;
193 GAIA_FOR(ids.size()) {
197 pPairIndex->pairIndexBuffer[pPairIndex->pairCnt] = (uint8_t)i;
198 ++pPairIndex->pairCnt;
199 add_pair_index_bucket(
200 pPairIndex->pairRelCountBuffer, pPairIndex->pairRelCountCnt, pPairIndex->pairRelIndexBuffer,
201 pairRelIndexCnt, (EntityId)ids[i].id(), (uint8_t)i);
202 add_pair_index_bucket(
203 pPairIndex->pairTgtCountBuffer, pPairIndex->pairTgtCountCnt, pPairIndex->pairTgtIndexBuffer,
204 pairTgtIndexCnt, (EntityId)ids[i].gen(), (uint8_t)i);
206 if (ids[i].
id() == Is.id())
207 pPairIndex->pairsAsIndexBuffer[pPairIndex->pairCntIs++] = (uint8_t)i;
210 if (pPairIndex->pairCnt == 0) {
218 static void destroy(PairIndexData* pPairIndex) {
219 if (pPairIndex ==
nullptr)
222 pPairIndex->~PairIndexData();
223 mem::AllocHelper::free(
"ArchetypePairIndex", pPairIndex);
226 static PairCountBucket&
227 ensure_pair_index_bucket(PairCountBucket* pBuckets, uint8_t& bucketCnt, EntityId
id, uint8_t start) {
228 GAIA_FOR(bucketCnt) {
229 if (pBuckets[i].
id ==
id)
233 GAIA_ASSERT(bucketCnt < ChunkHeader::MAX_COMPONENTS);
234 auto& bucket = pBuckets[bucketCnt++];
236 bucket.start = start;
241 static void add_pair_index_bucket(
242 PairCountBucket* pBuckets, uint8_t& bucketCnt, uint8_t* pIndexBuffer, uint8_t& indexCnt, EntityId
id,
244 auto& bucket = ensure_pair_index_bucket(pBuckets, bucketCnt,
id, indexCnt);
245 GAIA_ASSERT(indexCnt < ChunkHeader::MAX_COMPONENTS);
246 pIndexBuffer[indexCnt++] = idsIdx;
250 static uint32_t pair_count_from_buckets(
const PairCountBucket* pBuckets, uint8_t bucketCnt, EntityId
id) {
251 GAIA_FOR(bucketCnt) {
252 if (pBuckets[i].
id ==
id)
253 return pBuckets[i].count;
260 const PairCountBucket* pBuckets, uint8_t bucketCnt,
const uint8_t* pIndexBuffer, EntityId
id) {
261 GAIA_FOR(bucketCnt) {
262 if (pBuckets[i].
id !=
id)
265 return {pIndexBuffer + pBuckets[i].start, pBuckets[i].count};
271 GAIA_NODISCARD uint32_t pair_matches(EntitySpan ids, Entity pair)
const {
272 GAIA_ASSERT(pair.pair());
274 if (pair == Pair(All, All))
277 if (pair.id() == All.id())
278 return pair_count_from_buckets(pairTgtCountBuffer, pairTgtCountCnt, (EntityId)pair.gen());
280 if (pair.gen() == All.id())
281 return pair_count_from_buckets(pairRelCountBuffer, pairRelCountCnt, (EntityId)pair.id());
285 [pair](Entity entity) {
286 return entity == pair;
292 GAIA_NODISCARD Entity entity_from_pairs_as_idx(EntitySpan ids, uint32_t idx)
const {
293 const auto idsIdx = pairsAsIndexBuffer[idx];
298 return {&pairIndexBuffer[0], pairCnt};
302 return pair_indices_from_buckets(
303 pairRelCountBuffer, pairRelCountCnt, pairRelIndexBuffer, (EntityId)relation.id());
307 return pair_indices_from_buckets(
308 pairTgtCountBuffer, pairTgtCountCnt, pairTgtIndexBuffer, (EntityId)target.id());
313 const World& m_world;
315 const ComponentCache& m_cc;
317 uint32_t& m_worldVersion;
320 StorageData m_storage{};
324 RuntimeData m_runtime{};
325 PairIndexData* m_pPairIndex =
nullptr;
328 Archetype(
const World& world,
const ComponentCache& cc, uint32_t& worldVersion):
329 m_world(world), m_cc(cc), m_worldVersion(worldVersion)
334 for (
auto* pChunk: m_storage.chunks)
336 PairIndexData::destroy(m_pPairIndex);
342 void update_data_offsets(uintptr_t memoryAddress) {
343 uintptr_t offset = 0;
349 offset += mem::padding<alignof(ComponentVersion)>(memoryAddress);
351 const auto cnt = m_shape.properties.cntEntities + 1;
352 GAIA_ASSERT(offset < 256);
353 m_shape.dataOffsets.firstByte_Versions = (ChunkDataVersionOffset)offset;
354 offset +=
sizeof(ComponentVersion) * cnt;
359 offset += mem::padding<alignof(Entity)>(offset);
361 const auto cnt = m_shape.properties.cntEntities;
363 m_shape.dataOffsets.firstByte_CompEntities = (ChunkDataOffset)offset;
366 offset +=
sizeof(Entity) * ChunkHeader::MAX_COMPONENTS;
372 offset += mem::padding<alignof(ComponentRecord)>(offset);
374 const auto cnt = m_shape.properties.cntEntities;
377 m_shape.dataOffsets.firstByte_Records = (ChunkDataOffset)offset;
380 offset +=
sizeof(ComponentRecord) * cnt;
386 offset += mem::padding<alignof(Entity)>(offset);
387 m_shape.dataOffsets.firstByte_EntityData = (ChunkDataOffset)offset;
399 static bool est_max_entities_per_chunk(
400 uint32_t offs,
const ComponentCacheItem*
const* pItems, uint32_t cnt, uint32_t cap, uint32_t maxDataOffset) {
402 const auto comp = comp_from_item(pItems[i]);
403 if (!component_has_inline_data(comp))
406 const auto* pItem = pItems[i];
407 GAIA_ASSERT(pItem !=
nullptr);
410 offs = pItem->calc_new_mem_offset(offs, cap);
411 if (offs >= maxDataOffset)
418 static void reg_components(
419 Archetype& arch, EntitySpan ids,
const ComponentCacheItem*
const* pItems, uint8_t from, uint8_t to,
420 uint32_t& currOff, uint32_t count) {
421 auto& ofs = arch.m_shape.compOffs;
424 GAIA_FOR2(from, to) arch.m_shape.ids[i] = ids[i];
427 GAIA_FOR2(from, to) {
428 const auto comp = comp_from_item(pItems[i]);
429 const auto compIdx = i;
431 if (!component_has_inline_data(comp)) {
434 const auto alig = comp.alig();
435 currOff = mem::align(currOff, alig);
436 ofs[compIdx] = (ChunkDataOffset)currOff;
439 currOff += comp.size() * count;
445 Archetype(Archetype&&) =
delete;
446 Archetype(
const Archetype&) =
delete;
447 Archetype& operator=(Archetype&&) =
delete;
448 Archetype& operator=(
const Archetype&) =
delete;
450 void save(ser::serializer& s) {
451 s.save(m_storage.firstFreeChunkIdx);
452 s.save(m_runtime.listIdx);
454 s.save((uint32_t)m_storage.chunks.size());
455 for (
auto* pChunk: m_storage.chunks) {
456 s.save(pChunk->idx());
461 void load(ser::serializer& s) {
462 s.load(m_storage.firstFreeChunkIdx);
463 s.load(m_runtime.listIdx);
465 uint32_t chunkCnt = 0;
467 m_storage.chunks.resize(chunkCnt,
nullptr);
470 uint32_t chunkIdx = 0;
473 auto* pChunk = m_storage.chunks[chunkIdx];
475 if (pChunk ==
nullptr) {
476 pChunk = Chunk::create(
477 m_world, m_cc, chunkIdx,
478 m_shape.properties.capacity, m_shape.properties.cntEntities,
479 m_shape.properties.genEntities, m_shape.properties.chunkDataBytes,
480 m_worldVersion, m_shape.dataOffsets, m_shape.ids, m_shape.compItems, m_shape.compOffs);
481 m_storage.chunks[chunkIdx] = pChunk;
484 pChunk->set_idx(chunkIdx);
489 void list_idx(uint32_t idx) {
490 m_runtime.listIdx = idx;
493 uint32_t list_idx()
const {
494 return m_runtime.listIdx;
497 GAIA_NODISCARD
bool cmp_comps(
const ArchetypeLookupChecker& other)
const {
498 return detail::cmp_comps(ids_view(), other.m_comps);
501 GAIA_NODISCARD
static Archetype*
502 create(
const World& world, ArchetypeId archetypeId, uint32_t& worldVersion, EntitySpan ids) {
503 const auto& cc = comp_cache(world);
505 auto* newArch = mem::AllocHelper::alloc<Archetype>(
"Archetype");
506 (void)
new (newArch) Archetype(world, cc, worldVersion);
508 newArch->m_archetypeId = archetypeId;
509 newArch->m_shape.archetypeIdHash = ArchetypeIdLookupKey::calc(archetypeId);
514 newArch->m_shape.queryMask = build_entity_mask({ids.data(), ids.size()});
516 const auto cnt = (uint32_t)ids.size();
517 newArch->m_shape.properties.cntEntities = (uint8_t)ids.size();
519 auto compItems =
std::span(&newArch->m_shape.compItems[0], cnt);
521 const ComponentCacheItem* pItem =
nullptr;
525 Entity pairEntities[] = {pair_rel(world, ids[i]), pair_tgt(world, ids[i])};
526 const auto* pRelItem = cc.find(pairEntities[0]);
527 const auto* pTgtItem = cc.find(pairEntities[1]);
528 Component pairComponents[] = {comp_from_item(pRelItem), comp_from_item(pTgtItem)};
529 const uint32_t idx = (pairComponents[0].size() != 0U || pairComponents[1].size() == 0U) ? 0 : 1;
530 pItem = idx == 0 ? pRelItem : pTgtItem;
532 pItem = cc.find(ids[i]);
534 compItems[i] = pItem;
538 static auto ChunkDataAreaOffset = Chunk::chunk_data_area_offset();
539 newArch->update_data_offsets(
545 ChunkDataAreaOffset);
546 const auto& offs = newArch->m_shape.dataOffsets;
547 newArch->m_pPairIndex = PairIndexData::create(ids);
550 const auto entsCnt = (uint32_t)ids.size();
551 uint32_t entsGeneric = entsCnt;
553 for (
auto i = entsCnt - 1; i != (uint32_t)-1; --i) {
554 if (ids[i].kind() != EntityKind::EK_Uni)
560 uint32_t genCompsSize = 0;
561 uint32_t uniCompsSize = 0;
562 GAIA_FOR(entsGeneric) genCompsSize += comp_from_item(newArch->m_shape.compItems[i]).size();
563 GAIA_FOR2(entsGeneric, cnt) uniCompsSize += comp_from_item(newArch->m_shape.compItems[i]).size();
565 auto compute_max_entities_for_chunk = [&](uint32_t maxEntities, uint32_t dataLimit) -> uint32_t {
567 uint32_t high = maxEntities;
571 auto try_fit = [&](uint32_t count) ->
bool {
572 const uint32_t currOff = offs.firstByte_EntityData + (count *
sizeof(Entity));
574 if (!est_max_entities_per_chunk(currOff, newArch->m_shape.compItems, entsGeneric, count, dataLimit))
576 if (!est_max_entities_per_chunk(
577 currOff, newArch->m_shape.compItems + entsGeneric, cnt - entsGeneric, 1, dataLimit))
584 while (low <= high) {
585 uint32_t mid = (low + high) / 2;
600 constexpr uint32_t MinEntitiesPerChunk = 1536;
601 uint32_t maxGenItemsInArchetype = 0;
603 auto compute_max_entities_for_size_type = [&](uint32_t sizeType) -> uint32_t {
604 const uint32_t dataLimit = Chunk::chunk_data_bytes(mem_block_size(sizeType));
605 const uint32_t fixedSize = offs.firstByte_EntityData + uniCompsSize + 1;
606 GAIA_ASSERT(dataLimit > fixedSize);
610 const uint32_t itemSize = genCompsSize + (uint32_t)
sizeof(Entity);
611 const uint32_t maxEntities = (dataLimit - fixedSize) / itemSize;
612 return compute_max_entities_for_chunk(maxEntities > 0 ? maxEntities : 1, dataLimit);
615 if (archetypeId == 0) {
617 maxGenItemsInArchetype = compute_max_entities_for_size_type(2);
619 for (uint32_t sizeType = 0; sizeType < MemoryBlockSizeClasses; ++sizeType) {
620 maxGenItemsInArchetype = compute_max_entities_for_size_type(sizeType);
621 if (maxGenItemsInArchetype >= MinEntitiesPerChunk)
628 if (maxGenItemsInArchetype > ChunkHeader::MAX_CHUNK_ENTITIES)
629 maxGenItemsInArchetype = ChunkHeader::MAX_CHUNK_ENTITIES;
632 auto currOff = offs.firstByte_EntityData + ((uint32_t)
sizeof(Entity) * maxGenItemsInArchetype);
634 *newArch, ids, newArch->m_shape.compItems, (uint8_t)0, (uint8_t)entsGeneric, currOff,
635 maxGenItemsInArchetype);
637 *newArch, ids, newArch->m_shape.compItems, (uint8_t)entsGeneric, (uint8_t)ids.size(), currOff, 1);
639 newArch->m_shape.properties.capacity = (uint16_t)maxGenItemsInArchetype;
640 newArch->m_shape.properties.chunkDataBytes = (ChunkDataOffset)currOff;
641 newArch->m_shape.properties.genEntities = (uint8_t)entsGeneric;
646 void static destroy(Archetype* pArchetype) {
647 GAIA_ASSERT(pArchetype !=
nullptr);
648 pArchetype->~Archetype();
649 mem::AllocHelper::free(
"Archetype", pArchetype);
652 QueryMask queryMask()
const {
653 return m_shape.queryMask;
656 ArchetypeIdLookupKey::LookupHash id_hash()
const {
657 return m_shape.archetypeIdHash;
663 m_shape.hashLookup = hashLookup;
680 GAIA_ASSERT(!m_storage.chunks.empty());
682 const auto chunkIndex = pChunk->
idx();
685 GAIA_ASSERT(chunkIndex == core::get_index(m_storage.chunks, pChunk));
690 m_storage.chunks.back()->set_idx(chunkIndex);
691 core::swap_erase(m_storage.chunks, chunkIndex);
702 const auto chunkCnt = m_storage.chunks.
size();
705 for (uint32_t i = m_storage.firstFreeChunkIdx; i < m_storage.chunks.size(); ++i) {
706 auto* pChunk = m_storage.chunks[i];
707 GAIA_ASSERT(pChunk !=
nullptr);
708 const auto entityCnt = pChunk->size();
709 if (entityCnt < pChunk->capacity()) {
710 m_storage.firstFreeChunkIdx = i;
717 GAIA_ASSERT(chunkCnt < UINT32_MAX);
720 auto* pChunk = Chunk::create(
721 m_world, m_cc, chunkCnt,
722 m_shape.properties.capacity, m_shape.properties.cntEntities,
723 m_shape.properties.genEntities, m_shape.properties.chunkDataBytes,
724 m_worldVersion, m_shape.dataOffsets, m_shape.ids, m_shape.compItems, m_shape.compOffs);
726 m_storage.firstFreeChunkIdx = m_storage.chunks.size();
727 m_storage.chunks.push_back(pChunk);
736 GAIA_ASSERT(!m_storage.chunks.empty());
738 auto* pChunk = m_storage.chunks[m_storage.firstFreeChunkIdx];
739 if (pChunk->size() >= pChunk->capacity())
740 ++m_storage.firstFreeChunkIdx;
748 GAIA_ASSERT(!m_storage.chunks.empty());
750 if (chunkThatRemovedEntity.
idx() == m_storage.firstFreeChunkIdx)
753 if (chunkThatRemovedEntity.
idx() < m_storage.firstFreeChunkIdx) {
754 m_storage.firstFreeChunkIdx = chunkThatRemovedEntity.
idx();
758 auto* pChunk = m_storage.chunks[m_storage.firstFreeChunkIdx];
759 if (pChunk->size() >= pChunk->capacity())
760 ++m_storage.firstFreeChunkIdx;
769 try_update_free_chunk_idx(chunk);
777 remove_entity_raw(chunk, row, recs);
781 GAIA_NODISCARD
const Properties& props()
const {
782 return m_shape.properties;
786 return m_storage.chunks;
789 GAIA_NODISCARD LookupHash lookup_hash()
const {
790 return m_shape.hashLookup;
793 GAIA_NODISCARD EntitySpan ids_view()
const {
794 return {&m_shape.ids[0], m_shape.properties.cntEntities};
797 GAIA_NODISCARD ChunkDataOffsetSpan comp_offs_view()
const {
798 return {&m_shape.compOffs[0], m_shape.properties.cntEntities};
803 GAIA_NODISCARD uint32_t
pairs()
const {
804 return m_pPairIndex !=
nullptr ? m_pPairIndex->pairCnt : 0;
810 return m_pPairIndex !=
nullptr ? m_pPairIndex->pairCntIs : 0;
816 return m_pPairIndex !=
nullptr ? m_pPairIndex->pair_matches(ids_view(),
pair) : 0;
819 GAIA_NODISCARD
Entity entity_from_pairs_as_idx(uint32_t idx)
const {
820 GAIA_ASSERT(m_pPairIndex !=
nullptr);
821 return m_pPairIndex->entity_from_pairs_as_idx(ids_view(), idx);
840 return core::has_if(ids_view(), [&](
Entity e) {
845 void observed_terms_inc() {
846 GAIA_ASSERT(m_runtime.observedTermCnt < m_shape.properties.cntEntities);
847 ++m_runtime.observedTermCnt;
850 void observed_terms_dec() {
851 GAIA_ASSERT(m_runtime.observedTermCnt > 0);
852 --m_runtime.observedTermCnt;
855 GAIA_NODISCARD
bool has_observed_terms()
const {
856 return m_runtime.observedTermCnt != 0;
862 template <
typename T>
863 GAIA_NODISCARD
bool has()
const {
865 const auto rel = m_cc.get<
typename T::rel>().entity;
866 const auto tgt = m_cc.get<
typename T::tgt>().entity;
869 const auto* pComp = m_cc.find<T>();
870 return pComp !=
nullptr && has(pComp->entity);
881 template <
bool Enabled>
884 for (
const auto* pChunk: chunks()) {
889 if constexpr (Enabled) {
890 cnt = pChunk->size_enabled();
892 cnt = pChunk->size_disabled();
895 if (flatIdx < offset + cnt) {
896 if constexpr (Enabled) {
897 const auto idx = (uint32_t)(flatIdx - offset) + pChunk->size_disabled();
898 return pChunk->entity_view()[idx];
900 const auto idx = (uint32_t)(flatIdx - offset);
901 return pChunk->entity_view()[idx];
919 template <
bool Enabled>
922 for (
const auto* pChunk: chunks()) {
927 if constexpr (Enabled) {
928 cnt = pChunk->size_enabled();
930 cnt = pChunk->size_disabled();
933 if (flatIdx < offset + cnt) {
934 if constexpr (Enabled) {
935 const auto idx = (uint32_t)(flatIdx - offset) + pChunk->size_disabled();
936 const auto* pData = pChunk->comp_ptr(compIdx, idx);
937 outEntity = pChunk->entity_view()[idx];
940 const auto idx = (uint32_t)(flatIdx - offset);
941 const auto* pData = pChunk->comp_ptr(compIdx, idx);
942 outEntity = pChunk->entity_view()[idx];
955 template <
bool Enabled>
960 Entity pivotEntity = get_flat_entity<Enabled>(high);
963 for (
size_t j = low; j < high; ++j) {
964 Entity jEntity = get_flat_entity<Enabled>(j);
965 if (func(m_world, &jEntity, &pivotEntity) < 0) {
967 Entity iEntity = get_flat_entity<Enabled>(i);
968 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
975 Entity iEntity = get_flat_entity<Enabled>(i);
976 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
980 sort_entities_inter<Enabled>(low, i - 1, func);
981 sort_entities_inter<Enabled>(i + 1, high, func);
985 template <
bool Enabled>
987 const ComponentCacheItem* pItem, uint32_t compIdx,
size_t low,
size_t high, TSortByFunc func) {
992 const void* pPivotData = get_flat_comp_ptr<Enabled>(compIdx, high, pivotEntity);
995 for (
size_t j = low; j < high; ++j) {
997 const void* jData = get_flat_comp_ptr<Enabled>(compIdx, j, jEntity);
998 if (func(m_world, jData, pPivotData) < 0) {
1001 (void)get_flat_comp_ptr<Enabled>(compIdx, i, iEntity);
1002 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
1010 (void)get_flat_comp_ptr<Enabled>(compIdx, i, iEntity);
1011 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
1015 sort_entities_inter<Enabled>(pItem, compIdx, low, i - 1, func);
1016 sort_entities_inter<Enabled>(pItem, compIdx, i + 1, high, func);
1029 if (entity == EntityBad) {
1031 uint32_t entities = 0;
1032 for (
const auto* pChunk: m_storage.chunks)
1033 entities += pChunk->size_enabled();
1035 sort_entities_inter<true>(0, entities - 1, func);
1038 uint32_t entities = 0;
1039 for (
const auto* pChunk: m_storage.chunks)
1040 entities += pChunk->size_disabled();
1042 sort_entities_inter<false>(0, entities - 1, func);
1045 const auto* pItem = m_cc.find(entity);
1046 GAIA_ASSERT(pItem !=
nullptr &&
"Trying to sort by a component that has not been registered");
1047 if (pItem ==
nullptr)
1050 const auto compIdx = chunks()[0]->comp_idx(entity);
1052 uint32_t entities = 0;
1053 for (
const auto* pChunk: m_storage.chunks)
1054 entities += pChunk->size_enabled();
1056 sort_entities_inter<true>(pItem, compIdx, 0, entities - 1, func);
1059 uint32_t entities = 0;
1060 for (
const auto* pChunk: m_storage.chunks)
1061 entities += pChunk->size_disabled();
1063 sort_entities_inter<false>(pItem, compIdx, 0, entities - 1, func);
1075 GAIA_ASSERT(pArchetypeRight !=
this);
1077 m_edges.graph.add_edge_right(entity, pArchetypeRight->id(), pArchetypeRight->id_hash());
1078 pArchetypeRight->build_graph_edges_left(
this, entity);
1081 void build_graph_edges_left(
Archetype* pArchetypeLeft,
Entity entity) {
1083 GAIA_ASSERT(pArchetypeLeft !=
this);
1085 m_edges.graph.add_edge_left(entity, pArchetypeLeft->id(), pArchetypeLeft->id_hash());
1088 void del_graph_edges(Archetype* pArchetypeRight, Entity entity) {
1090 GAIA_ASSERT(pArchetypeRight !=
this);
1092 m_edges.graph.del_edge_right(entity);
1093 pArchetypeRight->del_graph_edges_left(
this, entity);
1096 void del_graph_edges_left([[maybe_unused]] Archetype* pArchetypeLeft, Entity entity) {
1098 GAIA_ASSERT(pArchetypeLeft !=
this);
1100 m_edges.graph.del_edge_left(entity);
1107 m_edges.graph.del_edge_right(entity);
1114 m_edges.graph.del_edge_left(entity);
1121 return m_edges.graph.find_edge_right(entity);
1128 return m_edges.graph.find_edge_left(entity);
1131 GAIA_NODISCARD
auto& right_edges() {
1132 return m_edges.graph.right_edges();
1135 GAIA_NODISCARD
const auto& right_edges()
const {
1136 return m_edges.graph.right_edges();
1139 GAIA_NODISCARD
auto& left_edges() {
1140 return m_edges.graph.left_edges();
1143 GAIA_NODISCARD
const auto& left_edges()
const {
1144 return m_edges.graph.left_edges();
1149 return m_storage.chunks.empty();
1154 m_runtime.deleteReq = 1;
1159 return m_runtime.deleteReq;
1166 GAIA_ASSERT(lifespan <= MAX_ARCHETYPE_LIFESPAN);
1168 m_runtime.lifespanCountdownMax = lifespan;
1174 return m_runtime.lifespanCountdownMax;
1179 return m_runtime.lifespanCountdown > 0;
1189 return m_runtime.dead == 1;
1194 GAIA_ASSERT(!dead());
1195 m_runtime.lifespanCountdown = m_runtime.lifespanCountdownMax;
1200 GAIA_ASSERT(!dead());
1201 m_runtime.lifespanCountdown = 0;
1202 m_runtime.deleteReq = 0;
1208 GAIA_ASSERT(dying());
1209 GAIA_ASSERT(m_runtime.lifespanCountdownMax > 0);
1210 --m_runtime.lifespanCountdown;
1216 return m_runtime.lifespanCountdownMax > 0 && !dying() && empty();
1219 static void diag_entity(
const World& world,
Entity entity) {
1220 if (entity.entity()) {
1221 const auto name = entity_name(world, entity);
1223 " ent [%u:%u] %.*s [%s]", entity.id(), entity.gen(), (
int)name.size(), name.empty() ?
"" : name.data(),
1224 EntityKindString[entity.kind()]);
1225 }
else if (entity.pair()) {
1226 const auto rel = entity_name(world, entity.id());
1227 const auto tgt = entity_name(world, entity.gen());
1229 " pair [%u:%u] %.*s -> %.*s", entity.id(), entity.gen(), (
int)rel.size(),
1230 rel.empty() ?
"" : rel.data(), (int)tgt.size(), tgt.empty() ?
"" : tgt.data());
1232 const auto& cc = comp_cache(world);
1233 const auto& desc = cc.get(entity);
1235 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %s [%s]", desc.hashLookup.hash,
1236 desc.comp.size(), desc.comp.alig(), desc.entity.id(), desc.entity.gen(), desc.name.str(),
1237 EntityKindString[entity.kind()]);
1241 static void diag_basic_info(
const World& world,
const Archetype& archetype) {
1242 auto ids = archetype.ids_view();
1245 uint32_t entCnt = 0;
1246 uint32_t entCntDisabled = 0;
1247 for (
const auto* chunk: archetype.m_storage.chunks) {
1248 entCnt += chunk->size();
1249 entCntDisabled += chunk->size_disabled();
1253 uint32_t genCompsSize = 0;
1254 uint32_t uniCompsSize = 0;
1256 const auto& p = archetype.props();
1257 GAIA_FOR(p.genEntities) genCompsSize += comp_from_item(archetype.m_shape.compItems[i]).size();
1258 GAIA_FOR2(p.genEntities, p.cntEntities)
1259 uniCompsSize += comp_from_item(archetype.m_shape.compItems[i]).size();
1262 const auto chunkBytes = Chunk::chunk_total_bytes(archetype.props().chunkDataBytes);
1263 const auto sizeType = mem_block_size_type(chunkBytes);
1264 const auto allocSize = mem_block_size(sizeType) / 1024;
1268 "hash:%016" PRIx64 ", "
1269 "chunks:%u (%uK), data:%u/%u/%u B, "
1270 "entities:%u/%u/%u",
1271 archetype.
id(), archetype.lookup_hash().hash, (uint32_t)archetype.chunks().size(), allocSize, genCompsSize,
1272 uniCompsSize, archetype.props().chunkDataBytes, entCnt, entCntDisabled, archetype.props().capacity);
1275 GAIA_LOG_N(
" Components - count:%u", (uint32_t)ids.size());
1276 for (
const auto ent: ids)
1277 diag_entity(world, ent);
1281 static void diag_graph_info(
const World& world,
const Archetype& archetype) {
1282 archetype.m_edges.graph.diag(world);
1285 static void diag_chunk_info(
const Archetype& archetype) {
1286 const auto& chunks = archetype.m_storage.chunks;
1290 GAIA_LOG_N(
" Chunks");
1291 for (
const auto* pChunk: chunks)
1295 static void diag_entity_info(
const World& world,
const Archetype& archetype) {
1296 const auto& chunks = archetype.m_storage.chunks;
1300 GAIA_LOG_N(
" Entities");
1301 bool noEntities =
true;
1302 for (
const auto* pChunk: chunks) {
1303 if (pChunk->empty())
1307 auto ev = pChunk->entity_view();
1308 for (
auto entity: ev)
1309 diag_entity(world, entity);
1319 diag_basic_info(world, archetype);
1320 diag_graph_info(world, archetype);
1321 diag_chunk_info(archetype);
1322 diag_entity_info(world, archetype);