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;
111 QueryMask queryMask{};
112 Properties properties{};
114 ChunkDataOffsets dataOffsets{};
116 Entity ids[ChunkHeader::MAX_COMPONENTS];
118 Component comps[ChunkHeader::MAX_COMPONENTS];
120 ChunkDataOffset compOffs[ChunkHeader::MAX_COMPONENTS];
125 cnt::darray<Chunk*> chunks;
127 uint32_t firstFreeChunkIdx = 0;
132 uint32_t listIdx = BadIndex;
134 uint8_t observedTermCnt = 0;
137 uint32_t deleteReq : 1;
141 uint32_t lifespanCountdownMax : ARCHETYPE_LIFESPAN_BITS;
143 uint32_t lifespanCountdown : ARCHETYPE_LIFESPAN_BITS;
145 RuntimeData(): deleteReq(0), dead(0), lifespanCountdownMax(1), lifespanCountdown(0) {}
150 ArchetypeGraph graph;
153 struct PairIndexData {
155 EntityId
id = IdentifierIdBad;
161 uint8_t pairIndexBuffer[ChunkHeader::MAX_COMPONENTS];
163 uint8_t pairsAsIndexBuffer[ChunkHeader::MAX_COMPONENTS];
164 uint8_t pairRelIndexBuffer[ChunkHeader::MAX_COMPONENTS];
165 uint8_t pairTgtIndexBuffer[ChunkHeader::MAX_COMPONENTS];
172 uint8_t pairCntIs = 0;
174 uint8_t pairRelCountCnt = 0;
176 uint8_t pairTgtCountCnt = 0;
178 static PairIndexData* create(
EntitySpan ids) {
179 auto* pPairIndex = mem::AllocHelper::alloc<PairIndexData>(
"ArchetypePairIndex");
180 (void)
new (pPairIndex) PairIndexData();
182 uint8_t pairRelIndexCnt = 0;
183 uint8_t pairTgtIndexCnt = 0;
185 GAIA_FOR(ids.size()) {
189 pPairIndex->pairIndexBuffer[pPairIndex->pairCnt] = (uint8_t)i;
190 ++pPairIndex->pairCnt;
191 append_pair_index_bucket(
192 pPairIndex->pairRelCountBuffer, pPairIndex->pairRelCountCnt, pPairIndex->pairRelIndexBuffer,
193 pairRelIndexCnt, (EntityId)ids[i].id(), (uint8_t)i);
194 append_pair_index_bucket(
195 pPairIndex->pairTgtCountBuffer, pPairIndex->pairTgtCountCnt, pPairIndex->pairTgtIndexBuffer,
196 pairTgtIndexCnt, (EntityId)ids[i].gen(), (uint8_t)i);
198 if (ids[i].
id() == Is.id())
199 pPairIndex->pairsAsIndexBuffer[pPairIndex->pairCntIs++] = (uint8_t)i;
202 if (pPairIndex->pairCnt == 0) {
210 static void destroy(PairIndexData* pPairIndex) {
211 if (pPairIndex ==
nullptr)
214 pPairIndex->~PairIndexData();
215 mem::AllocHelper::free(
"ArchetypePairIndex", pPairIndex);
218 static PairCountBucket&
219 ensure_pair_index_bucket(PairCountBucket* pBuckets, uint8_t& bucketCnt, EntityId
id, uint8_t start) {
220 GAIA_FOR(bucketCnt) {
221 if (pBuckets[i].
id ==
id)
225 GAIA_ASSERT(bucketCnt < ChunkHeader::MAX_COMPONENTS);
226 auto& bucket = pBuckets[bucketCnt++];
228 bucket.start = start;
233 static void append_pair_index_bucket(
234 PairCountBucket* pBuckets, uint8_t& bucketCnt, uint8_t* pIndexBuffer, uint8_t& indexCnt, EntityId
id,
236 auto& bucket = ensure_pair_index_bucket(pBuckets, bucketCnt,
id, indexCnt);
237 GAIA_ASSERT(indexCnt < ChunkHeader::MAX_COMPONENTS);
238 pIndexBuffer[indexCnt++] = idsIdx;
242 static uint32_t pair_count_from_buckets(
const PairCountBucket* pBuckets, uint8_t bucketCnt, EntityId
id) {
243 GAIA_FOR(bucketCnt) {
244 if (pBuckets[i].
id ==
id)
245 return pBuckets[i].count;
252 const PairCountBucket* pBuckets, uint8_t bucketCnt,
const uint8_t* pIndexBuffer, EntityId
id) {
253 GAIA_FOR(bucketCnt) {
254 if (pBuckets[i].
id !=
id)
257 return {pIndexBuffer + pBuckets[i].start, pBuckets[i].count};
263 GAIA_NODISCARD uint32_t pair_matches(EntitySpan ids, Entity pair)
const {
264 GAIA_ASSERT(pair.pair());
266 if (pair == Pair(All, All))
269 if (pair.id() == All.id())
270 return pair_count_from_buckets(pairTgtCountBuffer, pairTgtCountCnt, (EntityId)pair.gen());
272 if (pair.gen() == All.id())
273 return pair_count_from_buckets(pairRelCountBuffer, pairRelCountCnt, (EntityId)pair.id());
277 [pair](Entity entity) {
278 return entity == pair;
284 GAIA_NODISCARD Entity entity_from_pairs_as_idx(EntitySpan ids, uint32_t idx)
const {
285 const auto idsIdx = pairsAsIndexBuffer[idx];
290 return {&pairIndexBuffer[0], pairCnt};
294 return pair_indices_from_buckets(
295 pairRelCountBuffer, pairRelCountCnt, pairRelIndexBuffer, (EntityId)relation.id());
299 return pair_indices_from_buckets(
300 pairTgtCountBuffer, pairTgtCountCnt, pairTgtIndexBuffer, (EntityId)target.id());
305 const World& m_world;
307 const ComponentCache& m_cc;
309 uint32_t& m_worldVersion;
312 StorageData m_storage{};
316 RuntimeData m_runtime{};
317 PairIndexData* m_pPairIndex =
nullptr;
320 Archetype(
const World& world,
const ComponentCache& cc, uint32_t& worldVersion):
321 m_world(world), m_cc(cc), m_worldVersion(worldVersion)
326 for (
auto* pChunk: m_storage.chunks)
328 PairIndexData::destroy(m_pPairIndex);
334 void update_data_offsets(uintptr_t memoryAddress) {
335 uintptr_t offset = 0;
341 offset += mem::padding<alignof(ComponentVersion)>(memoryAddress);
343 const auto cnt = comps_view().size() + 1;
344 GAIA_ASSERT(offset < 256);
345 m_shape.dataOffsets.firstByte_Versions = (ChunkDataVersionOffset)offset;
346 offset +=
sizeof(ComponentVersion) * cnt;
351 offset += mem::padding<alignof(Entity)>(offset);
353 const auto cnt = comps_view().size();
355 m_shape.dataOffsets.firstByte_CompEntities = (ChunkDataOffset)offset;
358 offset +=
sizeof(Entity) * ChunkHeader::MAX_COMPONENTS;
364 offset += mem::padding<alignof(ComponentRecord)>(offset);
366 const auto cnt = comps_view().size();
369 m_shape.dataOffsets.firstByte_Records = (ChunkDataOffset)offset;
372 offset +=
sizeof(ComponentRecord) * cnt;
378 offset += mem::padding<alignof(Entity)>(offset);
379 m_shape.dataOffsets.firstByte_EntityData = (ChunkDataOffset)offset;
384 static bool est_max_entities_per_chunk(
385 const ComponentCache& cc, uint32_t offs, ComponentSpan comps, uint32_t cap, uint32_t maxDataOffset) {
386 for (
const auto comp: comps) {
387 if (!component_has_inline_data(comp))
390 const auto& desc = cc.get(comp.id());
393 offs = desc.calc_new_mem_offset(offs, cap);
394 if (offs >= maxDataOffset)
401 static void reg_components(
402 Archetype& arch, EntitySpan ids, ComponentSpan comps, uint8_t from, uint8_t to, uint32_t& currOff,
404 auto& ofs = arch.m_shape.compOffs;
407 GAIA_FOR2(from, to) arch.m_shape.ids[i] = ids[i];
410 GAIA_FOR2(from, to) {
411 const auto comp = comps[i];
412 const auto compIdx = i;
414 if (!component_has_inline_data(comp)) {
417 const auto alig = comp.alig();
418 currOff = mem::align(currOff, alig);
419 ofs[compIdx] = (ChunkDataOffset)currOff;
422 currOff += comp.size() * count;
428 Archetype(Archetype&&) =
delete;
429 Archetype(
const Archetype&) =
delete;
430 Archetype& operator=(Archetype&&) =
delete;
431 Archetype& operator=(
const Archetype&) =
delete;
433 void save(ser::serializer& s) {
434 s.save(m_storage.firstFreeChunkIdx);
435 s.save(m_runtime.listIdx);
437 s.save((uint32_t)m_storage.chunks.size());
438 for (
auto* pChunk: m_storage.chunks) {
439 s.save(pChunk->idx());
444 void load(ser::serializer& s) {
445 s.load(m_storage.firstFreeChunkIdx);
446 s.load(m_runtime.listIdx);
448 uint32_t chunkCnt = 0;
450 m_storage.chunks.resize(chunkCnt,
nullptr);
453 uint32_t chunkIdx = 0;
456 auto* pChunk = m_storage.chunks[chunkIdx];
458 if (pChunk ==
nullptr) {
459 pChunk = Chunk::create(
460 m_world, m_cc, chunkIdx,
461 m_shape.properties.capacity, m_shape.properties.cntEntities,
462 m_shape.properties.genEntities, m_shape.properties.chunkDataBytes,
463 m_worldVersion, m_shape.dataOffsets, m_shape.ids, m_shape.comps, m_shape.compOffs);
464 m_storage.chunks[chunkIdx] = pChunk;
467 pChunk->set_idx(chunkIdx);
472 void list_idx(uint32_t idx) {
473 m_runtime.listIdx = idx;
476 uint32_t list_idx()
const {
477 return m_runtime.listIdx;
480 GAIA_NODISCARD
bool cmp_comps(
const ArchetypeLookupChecker& other)
const {
481 return detail::cmp_comps(ids_view(), other.m_comps);
484 GAIA_NODISCARD
static Archetype*
485 create(
const World& world, ArchetypeId archetypeId, uint32_t& worldVersion, EntitySpan ids) {
486 const auto& cc = comp_cache(world);
488 auto* newArch = mem::AllocHelper::alloc<Archetype>(
"Archetype");
489 (void)
new (newArch) Archetype(world, cc, worldVersion);
491 newArch->m_archetypeId = archetypeId;
492 newArch->m_shape.archetypeIdHash = ArchetypeIdLookupKey::calc(archetypeId);
497 newArch->m_shape.queryMask = build_entity_mask({ids.data(), ids.size()});
499 const auto cnt = (uint32_t)ids.size();
500 newArch->m_shape.properties.cntEntities = (uint8_t)ids.size();
502 auto as_comp = [&](Entity entity) {
503 const auto* pDesc = cc.find(entity);
504 return pDesc ==
nullptr
505 ? Component(IdentifierIdBad, 0, 0, 0, DataStorageType::Table)
510 auto comps =
std::span(&newArch->m_shape.comps[0], cnt);
515 Entity pairEntities[] = {entity_from_id(world, ids[i].
id()), entity_from_id(world, ids[i].gen())};
516 Component pairComponents[] = {as_comp(pairEntities[0]), as_comp(pairEntities[1])};
517 const uint32_t idx = (pairComponents[0].size() != 0U || pairComponents[1].size() == 0U) ? 0 : 1;
518 comps[i] = pairComponents[idx];
520 comps[i] = as_comp(ids[i]);
525 static auto ChunkDataAreaOffset = Chunk::chunk_data_area_offset();
526 newArch->update_data_offsets(
532 ChunkDataAreaOffset);
533 const auto& offs = newArch->m_shape.dataOffsets;
534 newArch->m_pPairIndex = PairIndexData::create(ids);
537 const auto entsCnt = (uint32_t)ids.size();
538 uint32_t entsGeneric = entsCnt;
540 for (
auto i = entsCnt - 1; i != (uint32_t)-1; --i) {
541 if (ids[i].kind() != EntityKind::EK_Uni)
547 uint32_t genCompsSize = 0;
548 uint32_t uniCompsSize = 0;
549 GAIA_FOR(entsGeneric) genCompsSize += newArch->m_shape.comps[i].size();
550 GAIA_FOR2(entsGeneric, cnt) uniCompsSize += newArch->m_shape.comps[i].size();
552 auto compute_max_entities_for_chunk = [&](uint32_t maxEntities, uint32_t dataLimit) -> uint32_t {
554 uint32_t high = maxEntities;
558 auto try_fit = [&](uint32_t count) ->
bool {
559 const uint32_t currOff = offs.firstByte_EntityData + (count *
sizeof(Entity));
561 if (!est_max_entities_per_chunk(cc, currOff, comps.subspan(0, entsGeneric), count, dataLimit))
563 if (!est_max_entities_per_chunk(cc, currOff, comps.subspan(entsGeneric), 1, dataLimit))
570 while (low <= high) {
571 uint32_t mid = (low + high) / 2;
586 constexpr uint32_t MinEntitiesPerChunk = 384;
587 uint32_t maxGenItemsInArchetype = 0;
590 if (archetypeId == 0) {
591 const uint32_t size2 = Chunk::chunk_data_bytes(mem_block_size(2));
592 maxGenItemsInArchetype =
593 (size2 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
594 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size2);
595 if (maxGenItemsInArchetype > ChunkHeader::MAX_CHUNK_ENTITIES)
596 maxGenItemsInArchetype = ChunkHeader::MAX_CHUNK_ENTITIES;
600 const uint32_t size0 = Chunk::chunk_data_bytes(mem_block_size(0));
601 maxGenItemsInArchetype =
602 (size0 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
603 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size0);
606 if (maxGenItemsInArchetype < MinEntitiesPerChunk) {
607 const uint32_t size1 = Chunk::chunk_data_bytes(mem_block_size(1));
608 maxGenItemsInArchetype =
609 (size1 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
610 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size1);
614 if (maxGenItemsInArchetype < MinEntitiesPerChunk) {
615 const uint32_t size2 = Chunk::chunk_data_bytes(mem_block_size(2));
616 maxGenItemsInArchetype =
617 (size2 - offs.firstByte_EntityData - uniCompsSize - 1) / (genCompsSize + (uint32_t)
sizeof(Entity));
618 maxGenItemsInArchetype = compute_max_entities_for_chunk(maxGenItemsInArchetype, size2);
623 if (maxGenItemsInArchetype > ChunkHeader::MAX_CHUNK_ENTITIES)
624 maxGenItemsInArchetype = ChunkHeader::MAX_CHUNK_ENTITIES;
629 auto currOff = offs.firstByte_EntityData + ((uint32_t)
sizeof(Entity) * maxGenItemsInArchetype);
630 reg_components(*newArch, ids, comps, (uint8_t)0, (uint8_t)entsGeneric, currOff, maxGenItemsInArchetype);
631 reg_components(*newArch, ids, comps, (uint8_t)entsGeneric, (uint8_t)ids.size(), currOff, 1);
633 newArch->m_shape.properties.capacity = (uint16_t)maxGenItemsInArchetype;
634 newArch->m_shape.properties.chunkDataBytes = (ChunkDataOffset)currOff;
635 newArch->m_shape.properties.genEntities = (uint8_t)entsGeneric;
640 void static destroy(Archetype* pArchetype) {
641 GAIA_ASSERT(pArchetype !=
nullptr);
642 pArchetype->~Archetype();
643 mem::AllocHelper::free(
"Archetype", pArchetype);
646 QueryMask queryMask()
const {
647 return m_shape.queryMask;
650 ArchetypeIdLookupKey::LookupHash id_hash()
const {
651 return m_shape.archetypeIdHash;
657 m_shape.hashLookup = hashLookup;
674 GAIA_ASSERT(!m_storage.chunks.empty());
676 const auto chunkIndex = pChunk->
idx();
679 GAIA_ASSERT(chunkIndex == core::get_index(m_storage.chunks, pChunk));
684 m_storage.chunks.back()->set_idx(chunkIndex);
685 core::swap_erase(m_storage.chunks, chunkIndex);
696 const auto chunkCnt = m_storage.chunks.
size();
699 for (uint32_t i = m_storage.firstFreeChunkIdx; i < m_storage.chunks.size(); ++i) {
700 auto* pChunk = m_storage.chunks[i];
701 GAIA_ASSERT(pChunk !=
nullptr);
702 const auto entityCnt = pChunk->size();
703 if (entityCnt < pChunk->capacity()) {
704 m_storage.firstFreeChunkIdx = i;
711 GAIA_ASSERT(chunkCnt < UINT32_MAX);
714 auto* pChunk = Chunk::create(
715 m_world, m_cc, chunkCnt,
716 m_shape.properties.capacity, m_shape.properties.cntEntities,
717 m_shape.properties.genEntities, m_shape.properties.chunkDataBytes,
718 m_worldVersion, m_shape.dataOffsets, m_shape.ids, m_shape.comps, m_shape.compOffs);
720 m_storage.firstFreeChunkIdx = m_storage.chunks.size();
721 m_storage.chunks.push_back(pChunk);
730 GAIA_ASSERT(!m_storage.chunks.empty());
732 auto* pChunk = m_storage.chunks[m_storage.firstFreeChunkIdx];
733 if (pChunk->size() >= pChunk->capacity())
734 ++m_storage.firstFreeChunkIdx;
742 GAIA_ASSERT(!m_storage.chunks.empty());
744 if (chunkThatRemovedEntity.
idx() == m_storage.firstFreeChunkIdx)
747 if (chunkThatRemovedEntity.
idx() < m_storage.firstFreeChunkIdx) {
748 m_storage.firstFreeChunkIdx = chunkThatRemovedEntity.
idx();
752 auto* pChunk = m_storage.chunks[m_storage.firstFreeChunkIdx];
753 if (pChunk->size() >= pChunk->capacity())
754 ++m_storage.firstFreeChunkIdx;
763 try_update_free_chunk_idx(chunk);
771 remove_entity_raw(chunk, row, recs);
775 GAIA_NODISCARD
const Properties& props()
const {
776 return m_shape.properties;
780 return m_storage.chunks;
783 GAIA_NODISCARD LookupHash lookup_hash()
const {
784 return m_shape.hashLookup;
787 GAIA_NODISCARD EntitySpan ids_view()
const {
788 return {&m_shape.ids[0], m_shape.properties.cntEntities};
791 GAIA_NODISCARD ComponentSpan comps_view()
const {
792 return {&m_shape.comps[0], m_shape.properties.cntEntities};
795 GAIA_NODISCARD ChunkDataOffsetSpan comp_offs_view()
const {
796 return {&m_shape.compOffs[0], m_shape.properties.cntEntities};
801 GAIA_NODISCARD uint32_t
pairs()
const {
802 return m_pPairIndex !=
nullptr ? m_pPairIndex->pairCnt : 0;
808 return m_pPairIndex !=
nullptr ? m_pPairIndex->pairCntIs : 0;
814 return m_pPairIndex !=
nullptr ? m_pPairIndex->pair_matches(ids_view(),
pair) : 0;
817 GAIA_NODISCARD
Entity entity_from_pairs_as_idx(uint32_t idx)
const {
818 GAIA_ASSERT(m_pPairIndex !=
nullptr);
819 return m_pPairIndex->entity_from_pairs_as_idx(ids_view(), idx);
838 return core::has_if(ids_view(), [&](
Entity e) {
843 void observed_terms_inc() {
844 GAIA_ASSERT(m_runtime.observedTermCnt < m_shape.properties.cntEntities);
845 ++m_runtime.observedTermCnt;
848 void observed_terms_dec() {
849 GAIA_ASSERT(m_runtime.observedTermCnt > 0);
850 --m_runtime.observedTermCnt;
853 GAIA_NODISCARD
bool has_observed_terms()
const {
854 return m_runtime.observedTermCnt != 0;
860 template <
typename T>
861 GAIA_NODISCARD
bool has()
const {
863 const auto rel = m_cc.get<
typename T::rel>().entity;
864 const auto tgt = m_cc.get<
typename T::tgt>().entity;
867 const auto* pComp = m_cc.find<T>();
868 return pComp !=
nullptr && has(pComp->entity);
879 template <
bool Enabled>
882 for (
const auto* pChunk: chunks()) {
887 if constexpr (Enabled) {
888 cnt = pChunk->size_enabled();
890 cnt = pChunk->size_disabled();
893 if (flatIdx < offset + cnt) {
894 if constexpr (Enabled) {
895 const auto idx = (uint32_t)(flatIdx - offset) + pChunk->size_disabled();
896 return pChunk->entity_view()[idx];
898 const auto idx = (uint32_t)(flatIdx - offset);
899 return pChunk->entity_view()[idx];
917 template <
bool Enabled>
920 for (
const auto* pChunk: chunks()) {
925 if constexpr (Enabled) {
926 cnt = pChunk->size_enabled();
928 cnt = pChunk->size_disabled();
931 if (flatIdx < offset + cnt) {
932 if constexpr (Enabled) {
933 const auto idx = (uint32_t)(flatIdx - offset) + pChunk->size_disabled();
934 const auto* pData = pChunk->comp_ptr(compIdx, idx);
935 outEntity = pChunk->entity_view()[idx];
938 const auto idx = (uint32_t)(flatIdx - offset);
939 const auto* pData = pChunk->comp_ptr(compIdx, idx);
940 outEntity = pChunk->entity_view()[idx];
953 template <
bool Enabled>
958 Entity pivotEntity = get_flat_entity<Enabled>(high);
961 for (
size_t j = low; j < high; ++j) {
962 Entity jEntity = get_flat_entity<Enabled>(j);
963 if (func(m_world, &jEntity, &pivotEntity) < 0) {
965 Entity iEntity = get_flat_entity<Enabled>(i);
966 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
973 Entity iEntity = get_flat_entity<Enabled>(i);
974 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
978 sort_entities_inter<Enabled>(low, i - 1, func);
979 sort_entities_inter<Enabled>(i + 1, high, func);
983 template <
bool Enabled>
985 const ComponentCacheItem* pItem, uint32_t compIdx,
size_t low,
size_t high, TSortByFunc func) {
990 const void* pPivotData = get_flat_comp_ptr<Enabled>(compIdx, high, pivotEntity);
993 for (
size_t j = low; j < high; ++j) {
995 const void* jData = get_flat_comp_ptr<Enabled>(compIdx, j, jEntity);
996 if (func(m_world, jData, pPivotData) < 0) {
999 (void)get_flat_comp_ptr<Enabled>(compIdx, i, iEntity);
1000 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
1008 (void)get_flat_comp_ptr<Enabled>(compIdx, i, iEntity);
1009 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
1013 sort_entities_inter<Enabled>(pItem, compIdx, low, i - 1, func);
1014 sort_entities_inter<Enabled>(pItem, compIdx, i + 1, high, func);
1027 if (entity == EntityBad) {
1029 uint32_t entities = 0;
1030 for (
const auto* pChunk: m_storage.chunks)
1031 entities += pChunk->size_enabled();
1033 sort_entities_inter<true>(0, entities - 1, func);
1036 uint32_t entities = 0;
1037 for (
const auto* pChunk: m_storage.chunks)
1038 entities += pChunk->size_disabled();
1040 sort_entities_inter<false>(0, entities - 1, func);
1043 const auto* pItem = m_cc.find(entity);
1044 GAIA_ASSERT(pItem !=
nullptr &&
"Trying to sort by a component that has not been registered");
1045 if (pItem ==
nullptr)
1048 const auto compIdx = chunks()[0]->comp_idx(entity);
1050 uint32_t entities = 0;
1051 for (
const auto* pChunk: m_storage.chunks)
1052 entities += pChunk->size_enabled();
1054 sort_entities_inter<true>(pItem, compIdx, 0, entities - 1, func);
1057 uint32_t entities = 0;
1058 for (
const auto* pChunk: m_storage.chunks)
1059 entities += pChunk->size_disabled();
1061 sort_entities_inter<false>(pItem, compIdx, 0, entities - 1, func);
1073 GAIA_ASSERT(pArchetypeRight !=
this);
1075 m_edges.graph.add_edge_right(entity, pArchetypeRight->id(), pArchetypeRight->id_hash());
1076 pArchetypeRight->build_graph_edges_left(
this, entity);
1079 void build_graph_edges_left(
Archetype* pArchetypeLeft,
Entity entity) {
1081 GAIA_ASSERT(pArchetypeLeft !=
this);
1083 m_edges.graph.add_edge_left(entity, pArchetypeLeft->id(), pArchetypeLeft->id_hash());
1086 void del_graph_edges(Archetype* pArchetypeRight, Entity entity) {
1088 GAIA_ASSERT(pArchetypeRight !=
this);
1090 m_edges.graph.del_edge_right(entity);
1091 pArchetypeRight->del_graph_edges_left(
this, entity);
1094 void del_graph_edges_left([[maybe_unused]] Archetype* pArchetypeLeft, Entity entity) {
1096 GAIA_ASSERT(pArchetypeLeft !=
this);
1098 m_edges.graph.del_edge_left(entity);
1105 m_edges.graph.del_edge_right(entity);
1112 m_edges.graph.del_edge_left(entity);
1119 return m_edges.graph.find_edge_right(entity);
1126 return m_edges.graph.find_edge_left(entity);
1129 GAIA_NODISCARD
auto& right_edges() {
1130 return m_edges.graph.right_edges();
1133 GAIA_NODISCARD
const auto& right_edges()
const {
1134 return m_edges.graph.right_edges();
1137 GAIA_NODISCARD
auto& left_edges() {
1138 return m_edges.graph.left_edges();
1141 GAIA_NODISCARD
const auto& left_edges()
const {
1142 return m_edges.graph.left_edges();
1147 return m_storage.chunks.empty();
1152 m_runtime.deleteReq = 1;
1157 return m_runtime.deleteReq;
1164 GAIA_ASSERT(lifespan <= MAX_ARCHETYPE_LIFESPAN);
1166 m_runtime.lifespanCountdownMax = lifespan;
1172 return m_runtime.lifespanCountdownMax;
1177 return m_runtime.lifespanCountdown > 0;
1187 return m_runtime.dead == 1;
1192 GAIA_ASSERT(!dead());
1193 m_runtime.lifespanCountdown = m_runtime.lifespanCountdownMax;
1198 GAIA_ASSERT(!dead());
1199 m_runtime.lifespanCountdown = 0;
1200 m_runtime.deleteReq = 0;
1206 GAIA_ASSERT(dying());
1207 GAIA_ASSERT(m_runtime.lifespanCountdownMax > 0);
1208 --m_runtime.lifespanCountdown;
1214 return m_runtime.lifespanCountdownMax > 0 && !dying() && empty();
1217 static void diag_entity(
const World& world,
Entity entity) {
1218 if (entity.entity()) {
1219 const auto name = entity_name(world, entity);
1221 " ent [%u:%u] %.*s [%s]", entity.id(), entity.gen(), (
int)name.size(), name.empty() ?
"" : name.data(),
1222 EntityKindString[entity.kind()]);
1223 }
else if (entity.pair()) {
1224 const auto rel = entity_name(world, entity.id());
1225 const auto tgt = entity_name(world, entity.gen());
1227 " pair [%u:%u] %.*s -> %.*s", entity.id(), entity.gen(), (
int)rel.size(),
1228 rel.empty() ?
"" : rel.data(), (int)tgt.size(), tgt.empty() ?
"" : tgt.data());
1230 const auto& cc = comp_cache(world);
1231 const auto& desc = cc.get(entity);
1233 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %s [%s]", desc.hashLookup.hash,
1234 desc.comp.size(), desc.comp.alig(), desc.entity.id(), desc.entity.gen(), desc.name.str(),
1235 EntityKindString[entity.kind()]);
1239 static void diag_basic_info(
const World& world,
const Archetype& archetype) {
1240 auto ids = archetype.ids_view();
1241 auto comps = archetype.comps_view();
1244 uint32_t entCnt = 0;
1245 uint32_t entCntDisabled = 0;
1246 for (
const auto* chunk: archetype.m_storage.chunks) {
1247 entCnt += chunk->size();
1248 entCntDisabled += chunk->size_disabled();
1252 uint32_t genCompsSize = 0;
1253 uint32_t uniCompsSize = 0;
1255 const auto& p = archetype.props();
1256 GAIA_FOR(p.genEntities) genCompsSize += comps[i].size();
1257 GAIA_FOR2(p.genEntities, comps.size()) uniCompsSize += comps[i].size();
1260 const auto chunkBytes = Chunk::chunk_total_bytes(archetype.props().chunkDataBytes);
1261 const auto sizeType = mem_block_size_type(chunkBytes);
1262 const auto allocSize = mem_block_size(sizeType) / 1024;
1266 "hash:%016" PRIx64 ", "
1267 "chunks:%u (%uK), data:%u/%u/%u B, "
1268 "entities:%u/%u/%u",
1269 archetype.
id(), archetype.lookup_hash().hash, (uint32_t)archetype.chunks().size(), allocSize, genCompsSize,
1270 uniCompsSize, archetype.props().chunkDataBytes, entCnt, entCntDisabled, archetype.props().capacity);
1273 GAIA_LOG_N(
" Components - count:%u", (uint32_t)ids.size());
1274 for (
const auto ent: ids)
1275 diag_entity(world, ent);
1279 static void diag_graph_info(
const World& world,
const Archetype& archetype) {
1280 archetype.m_edges.graph.diag(world);
1283 static void diag_chunk_info(
const Archetype& archetype) {
1284 const auto& chunks = archetype.m_storage.chunks;
1288 GAIA_LOG_N(
" Chunks");
1289 for (
const auto* pChunk: chunks)
1293 static void diag_entity_info(
const World& world,
const Archetype& archetype) {
1294 const auto& chunks = archetype.m_storage.chunks;
1298 GAIA_LOG_N(
" Entities");
1299 bool noEntities =
true;
1300 for (
const auto* pChunk: chunks) {
1301 if (pChunk->empty())
1305 auto ev = pChunk->entity_view();
1306 for (
auto entity: ev)
1307 diag_entity(world, entity);
1317 diag_basic_info(world, archetype);
1318 diag_graph_info(world, archetype);
1319 diag_chunk_info(archetype);
1320 diag_entity_info(world, archetype);