2#include "gaia/config/config.h"
7#include "gaia/cnt/darray.h"
9#include "gaia/core/hashing_policy.h"
10#include "gaia/core/utility.h"
11#include "gaia/ecs/api.h"
12#include "gaia/ecs/archetype_common.h"
13#include "gaia/ecs/archetype_graph.h"
14#include "gaia/ecs/chunk.h"
15#include "gaia/ecs/chunk_allocator.h"
16#include "gaia/ecs/chunk_header.h"
17#include "gaia/ecs/component.h"
18#include "gaia/ecs/component_cache.h"
19#include "gaia/ecs/id.h"
20#include "gaia/ecs/query_mask.h"
21#include "gaia/mem/mem_alloc.h"
22#include "gaia/ser/ser_binary.h"
28 struct EntityContainer;
32 GAIA_NODISCARD
inline bool cmp_comps(EntitySpan comps, EntitySpan compsOther) {
33 const auto s0 = comps.size();
34 const auto s1 = compsOther.size();
42 if (comps[i] != compsOther[i])
72 GAIA_NODISCARD ArchetypeId
id()
const {
91 return detail::cmp_comps(m_comps, other.m_comps);
103 static constexpr uint16_t ARCHETYPE_LIFESPAN_BITS = 7;
105 static_assert(ARCHETYPE_LIFESPAN_BITS >= ChunkHeader::CHUNK_LIFESPAN_BITS);
107 static constexpr uint16_t MAX_ARCHETYPE_LIFESPAN = (1 << ARCHETYPE_LIFESPAN_BITS) - 1;
120 GAIA_NODISCARD
static Component empty_comp() noexcept {
121 return Component(IdentifierIdBad, 0, 0, 0, DataStorageType::Table);
124 GAIA_NODISCARD
static Component comp_from_item(
Entity term,
const ComponentCacheItem* pItem)
noexcept {
125 return pItem ==
nullptr ? empty_comp() : archetype_component(term, pItem->comp);
129 ArchetypeIdLookupKey::LookupHash archetypeIdHash;
131 LookupHash hashLookup = {0};
133 QueryMask queryMask{};
134 Properties properties{};
136 ChunkDataOffsets dataOffsets{};
138 Entity ids[ChunkHeader::MAX_COMPONENTS];
140 const ComponentCacheItem* compItems[ChunkHeader::MAX_COMPONENTS]{};
142 ChunkDataOffset compOffs[ChunkHeader::MAX_COMPONENTS];
147 cnt::darray<Chunk*> chunks;
149 uint32_t firstFreeChunkIdx = 0;
154 uint32_t listIdx = BadIndex;
156 uint8_t observedTermCnt = 0;
159 uint32_t deleteReq : 1;
163 uint32_t lifespanCountdownMax : ARCHETYPE_LIFESPAN_BITS;
165 uint32_t lifespanCountdown : ARCHETYPE_LIFESPAN_BITS;
167 RuntimeData(): deleteReq(0), dead(0), lifespanCountdownMax(1), lifespanCountdown(0) {}
172 ArchetypeGraph graph;
175 struct PairIndexData {
186 PairIndexData() noexcept {}
189 uint8_t pairIndexBuffer[ChunkHeader::MAX_COMPONENTS];
191 uint8_t pairsAsIndexBuffer[ChunkHeader::MAX_COMPONENTS];
192 uint8_t pairRelIndexBuffer[ChunkHeader::MAX_COMPONENTS];
193 uint8_t pairTgtIndexBuffer[ChunkHeader::MAX_COMPONENTS];
195 PairCountBucket pairRelCountBuffer[ChunkHeader::MAX_COMPONENTS];
196 PairCountBucket pairTgtCountBuffer[ChunkHeader::MAX_COMPONENTS];
200 uint8_t pairCntIs = 0;
202 uint8_t pairRelCountCnt = 0;
204 uint8_t pairTgtCountCnt = 0;
206 static PairIndexData* create(EntitySpan ids) {
207 auto* pPairIndex = mem::AllocHelper::alloc<PairIndexData>(
"ArchetypePairIndex");
208 (void)
new (pPairIndex) PairIndexData();
210 uint8_t pairRelIndexCnt = 0;
211 uint8_t pairTgtIndexCnt = 0;
213 GAIA_FOR(ids.size()) {
217 pPairIndex->pairIndexBuffer[pPairIndex->pairCnt] = (uint8_t)i;
218 ++pPairIndex->pairCnt;
219 add_pair_index_bucket(
220 pPairIndex->pairRelCountBuffer, pPairIndex->pairRelCountCnt, pPairIndex->pairRelIndexBuffer,
221 pairRelIndexCnt, (EntityId)ids[i].id(), (uint8_t)i);
222 add_pair_index_bucket(
223 pPairIndex->pairTgtCountBuffer, pPairIndex->pairTgtCountCnt, pPairIndex->pairTgtIndexBuffer,
224 pairTgtIndexCnt, (EntityId)ids[i].gen(), (uint8_t)i);
226 if (ids[i].
id() == Is.
id())
227 pPairIndex->pairsAsIndexBuffer[pPairIndex->pairCntIs++] = (uint8_t)i;
230 if (pPairIndex->pairCnt == 0) {
238 static void destroy(PairIndexData* pPairIndex) {
239 if (pPairIndex ==
nullptr)
242 pPairIndex->~PairIndexData();
243 mem::AllocHelper::free(
"ArchetypePairIndex", pPairIndex);
246 static PairCountBucket&
247 ensure_pair_index_bucket(PairCountBucket* pBuckets, uint8_t& bucketCnt, EntityId
id, uint8_t start) {
248 GAIA_FOR(bucketCnt) {
249 if (pBuckets[i].
id ==
id)
253 GAIA_ASSERT(bucketCnt < ChunkHeader::MAX_COMPONENTS);
254 auto& bucket = pBuckets[bucketCnt++];
256 bucket.start = start;
261 static void add_pair_index_bucket(
262 PairCountBucket* pBuckets, uint8_t& bucketCnt, uint8_t* pIndexBuffer, uint8_t& indexCnt, EntityId
id,
264 auto& bucket = ensure_pair_index_bucket(pBuckets, bucketCnt,
id, indexCnt);
265 GAIA_ASSERT(indexCnt < ChunkHeader::MAX_COMPONENTS);
266 pIndexBuffer[indexCnt++] = idsIdx;
270 static uint32_t pair_count_from_buckets(
const PairCountBucket* pBuckets, uint8_t bucketCnt, EntityId
id) {
271 GAIA_FOR(bucketCnt) {
272 if (pBuckets[i].
id ==
id)
273 return pBuckets[i].count;
279 GAIA_NODISCARD
static std::span<const uint8_t> pair_indices_from_buckets(
280 const PairCountBucket* pBuckets, uint8_t bucketCnt,
const uint8_t* pIndexBuffer, EntityId
id) {
281 GAIA_FOR(bucketCnt) {
282 if (pBuckets[i].
id !=
id)
285 return {pIndexBuffer + pBuckets[i].start, pBuckets[i].count};
291 GAIA_NODISCARD uint32_t pair_matches(EntitySpan ids, Entity pair)
const {
292 GAIA_ASSERT(pair.pair());
294 if (pair == Pair(All, All))
297 if (pair.id() == All.
id())
298 return pair_count_from_buckets(pairTgtCountBuffer, pairTgtCountCnt, (EntityId)pair.gen());
300 if (pair.gen() == All.
id())
301 return pair_count_from_buckets(pairRelCountBuffer, pairRelCountCnt, (EntityId)pair.id());
305 [pair](Entity entity) {
306 return entity == pair;
312 GAIA_NODISCARD Entity entity_from_pairs_as_idx(EntitySpan ids, uint32_t idx)
const {
313 const auto idsIdx = pairsAsIndexBuffer[idx];
317 GAIA_NODISCARD std::span<const uint8_t> pair_indices()
const {
318 return {&pairIndexBuffer[0], pairCnt};
321 GAIA_NODISCARD std::span<const uint8_t> pair_rel_indices(Entity relation)
const {
322 return pair_indices_from_buckets(
323 pairRelCountBuffer, pairRelCountCnt, pairRelIndexBuffer, (EntityId)relation.id());
326 GAIA_NODISCARD std::span<const uint8_t> pair_tgt_indices(Entity target)
const {
327 return pair_indices_from_buckets(
328 pairTgtCountBuffer, pairTgtCountCnt, pairTgtIndexBuffer, (EntityId)target.
id());
333 const World& m_world;
335 const ComponentCache& m_cc;
337 uint32_t& m_worldVersion;
340 StorageData m_storage{};
344 RuntimeData m_runtime{};
345 PairIndexData* m_pPairIndex =
nullptr;
348 Archetype(
const World& world,
const ComponentCache& cc, uint32_t& worldVersion):
349 m_world(world), m_cc(cc), m_worldVersion(worldVersion)
354 for (
auto* pChunk: m_storage.chunks)
356 PairIndexData::destroy(m_pPairIndex);
362 void update_data_offsets() {
363 uintptr_t offset = 0;
369 const auto cnt = (uint32_t)m_shape.properties.cntEntities + 1U;
370 GAIA_ASSERT(offset < 256);
371 m_shape.dataOffsets.firstByte_Versions = (ChunkDataVersionOffset)offset;
372 offset +=
sizeof(ComponentVersion) * cnt;
377 offset += mem::padding<alignof(Entity)>(offset);
379 const auto cnt = m_shape.properties.cntEntities;
381 m_shape.dataOffsets.firstByte_CompEntities = (ChunkDataOffset)offset;
384 offset +=
sizeof(Entity) * ChunkHeader::MAX_COMPONENTS;
390 offset += mem::padding<alignof(ComponentRecord)>(offset);
392 const auto cnt = m_shape.properties.cntEntities;
395 m_shape.dataOffsets.firstByte_Records = (ChunkDataOffset)offset;
398 offset +=
sizeof(ComponentRecord) * cnt;
404 offset += mem::padding<alignof(Entity)>(offset);
405 m_shape.dataOffsets.firstByte_EntityData = (ChunkDataOffset)offset;
417 static bool est_max_entities_per_chunk(
418 uint32_t offs,
const Entity* ids,
const ComponentCacheItem*
const* pItems, uint32_t cnt, uint32_t cap,
419 uint32_t maxDataOffset) {
421 const auto comp = comp_from_item(ids[i], pItems[i]);
422 if (!component_uses_table_storage(comp))
425 const auto* pItem = pItems[i];
426 GAIA_ASSERT(pItem !=
nullptr);
429 offs = pItem->calc_new_mem_offset(offs, cap);
430 if (offs >= maxDataOffset)
437 static void reg_components(
438 Archetype& arch, EntitySpan ids,
const ComponentCacheItem*
const* pItems, uint8_t from, uint8_t to,
439 uint32_t& currOff, uint32_t count) {
440 auto& ofs = arch.m_shape.compOffs;
443 GAIA_FOR2(from, to) arch.m_shape.ids[i] = ids[i];
446 GAIA_FOR2(from, to) {
447 const auto comp = comp_from_item(ids[i], pItems[i]);
448 const auto compIdx = i;
450 if (!component_uses_table_storage(comp)) {
453 const auto alig = comp.alig();
455 GAIA_ASSERT(alig <= MemoryBlockAlignment);
456 currOff = mem::align(currOff, alig);
457 ofs[compIdx] = (ChunkDataOffset)currOff;
459 currOff = pItems[i]->calc_new_mem_offset(currOff, count);
465 Archetype(Archetype&&) =
delete;
466 Archetype(
const Archetype&) =
delete;
467 Archetype& operator=(Archetype&&) =
delete;
468 Archetype& operator=(
const Archetype&) =
delete;
473 s.
save(m_storage.firstFreeChunkIdx);
474 s.
save(m_runtime.listIdx);
476 s.
save((uint32_t)m_storage.chunks.size());
477 for (
auto* pChunk: m_storage.chunks) {
478 s.
save(pChunk->idx());
486 s.
load(m_storage.firstFreeChunkIdx);
487 s.
load(m_runtime.listIdx);
489 uint32_t chunkCnt = 0;
491 m_storage.chunks.resize(chunkCnt,
nullptr);
494 uint32_t chunkIdx = 0;
497 auto* pChunk = m_storage.chunks[chunkIdx];
499 if (pChunk ==
nullptr) {
500 pChunk = Chunk::create(
501 m_world, m_cc, chunkIdx,
502 m_shape.properties.capacity, m_shape.properties.cntEntities,
503 m_shape.properties.chunkDataBytes,
504 m_worldVersion, m_shape.dataOffsets, m_shape.ids, m_shape.compItems, m_shape.compOffs);
505 m_storage.chunks[chunkIdx] = pChunk;
508 pChunk->set_idx(chunkIdx);
516 m_runtime.listIdx = idx;
522 return m_runtime.listIdx;
529 return detail::cmp_comps(ids_view(), other.m_comps);
539 create(
const World& world, ArchetypeId archetypeId, uint32_t& worldVersion, EntitySpan ids) {
540 const auto& cc = comp_cache(world);
542 auto* newArch = mem::AllocHelper::alloc<Archetype>(
"Archetype");
543 (void)
new (newArch)
Archetype(world, cc, worldVersion);
545 newArch->m_archetypeId = archetypeId;
546 newArch->m_shape.archetypeIdHash = ArchetypeIdLookupKey::calc(archetypeId);
551 newArch->m_shape.queryMask = build_entity_mask({ids.data(), ids.size()});
553 const auto cnt = (uint32_t)ids.size();
554 newArch->m_shape.properties.cntEntities = (uint8_t)ids.size();
556 auto compItems = std::span(&newArch->m_shape.compItems[0], cnt);
557 GAIA_FOR(cnt) compItems[i] = ids[i].pair() ? cc.find_pair_payload(ids[i]) : cc.find(ids[i]);
560 newArch->update_data_offsets();
561 const auto& offs = newArch->m_shape.dataOffsets;
562 newArch->m_pPairIndex = PairIndexData::create(ids);
564 uint32_t compsSize = 0;
566 compsSize += comp_from_item(ids[i], newArch->m_shape.compItems[i]).size();
568 auto compute_max_entities_for_chunk = [&](uint32_t maxEntities, uint32_t dataLimit) -> uint32_t {
570 uint32_t high = maxEntities;
574 auto try_fit = [&](uint32_t count) ->
bool {
575 const uint32_t currOff = offs.firstByte_EntityData + (count *
sizeof(
Entity));
576 return est_max_entities_per_chunk(currOff, ids.data(), newArch->m_shape.compItems, cnt, count, dataLimit);
580 while (low <= high) {
581 uint32_t mid = (low + high) / 2;
596 constexpr uint32_t MinEntitiesPerChunk = 1536;
597 uint32_t maxItemsInArchetype = 0;
599 auto compute_max_entities_for_size_type = [&](uint32_t sizeType) -> uint32_t {
600 const uint32_t dataLimit = Chunk::chunk_data_bytes(mem_block_size(sizeType));
601 const uint32_t fixedSize = offs.firstByte_EntityData + 1;
602 GAIA_ASSERT(dataLimit > fixedSize);
606 const uint32_t itemSize = compsSize + (uint32_t)
sizeof(
Entity);
607 const uint32_t maxEntities = (dataLimit - fixedSize) / itemSize;
608 return compute_max_entities_for_chunk(maxEntities > 0 ? maxEntities : 1, dataLimit);
611 if (archetypeId == 0) {
613 maxItemsInArchetype = compute_max_entities_for_size_type(2);
615 for (uint32_t sizeType = 0; sizeType < MemoryBlockSizeClasses; ++sizeType) {
616 maxItemsInArchetype = compute_max_entities_for_size_type(sizeType);
617 if (maxItemsInArchetype >= MinEntitiesPerChunk)
624 if (maxItemsInArchetype > ChunkHeader::MAX_CHUNK_ENTITIES)
625 maxItemsInArchetype = ChunkHeader::MAX_CHUNK_ENTITIES;
628 auto currOff = offs.firstByte_EntityData + ((uint32_t)
sizeof(
Entity) * maxItemsInArchetype);
630 *newArch, ids, newArch->m_shape.compItems, (uint8_t)0, (uint8_t)ids.size(), currOff, maxItemsInArchetype);
632 newArch->m_shape.properties.capacity = (uint16_t)maxItemsInArchetype;
633 newArch->m_shape.properties.chunkDataBytes = (ChunkDataOffset)currOff;
641 GAIA_ASSERT(pArchetype !=
nullptr);
642 pArchetype->~Archetype();
643 mem::AllocHelper::free(
"Archetype", pArchetype);
649 return m_shape.queryMask;
654 ArchetypeIdLookupKey::LookupHash
id_hash()
const {
655 return m_shape.archetypeIdHash;
661 m_shape.hashLookup = hashLookup;
678 GAIA_ASSERT(!m_storage.chunks.empty());
680 const auto chunkIndex = pChunk->
idx();
683 GAIA_ASSERT(chunkIndex == core::get_index(m_storage.chunks, pChunk));
688 m_storage.chunks.back()->set_idx(chunkIndex);
689 core::swap_erase(m_storage.chunks, chunkIndex);
701 const auto chunkCnt = m_storage.chunks.
size();
704 for (uint32_t i = m_storage.firstFreeChunkIdx; i < m_storage.chunks.size(); ++i) {
705 auto* pChunk = m_storage.chunks[i];
706 GAIA_ASSERT(pChunk !=
nullptr);
707 const auto entityCnt = pChunk->size();
708 if (entityCnt < pChunk->capacity()) {
709 m_storage.firstFreeChunkIdx = i;
716 GAIA_ASSERT(chunkCnt < UINT32_MAX);
719 auto* pChunk = Chunk::create(
720 m_world, m_cc, chunkCnt,
721 m_shape.properties.capacity, m_shape.properties.cntEntities,
722 m_shape.properties.chunkDataBytes,
723 m_worldVersion, m_shape.dataOffsets, m_shape.ids, m_shape.compItems, m_shape.compOffs);
725 m_storage.firstFreeChunkIdx = m_storage.chunks.size();
726 m_storage.chunks.push_back(pChunk);
735 GAIA_ASSERT(!m_storage.chunks.empty());
737 auto* pChunk = m_storage.chunks[m_storage.firstFreeChunkIdx];
738 if (pChunk->size() >= pChunk->capacity())
739 ++m_storage.firstFreeChunkIdx;
747 GAIA_ASSERT(!m_storage.chunks.empty());
749 if (chunkThatRemovedEntity.
idx() == m_storage.firstFreeChunkIdx)
752 if (chunkThatRemovedEntity.
idx() < m_storage.firstFreeChunkIdx) {
753 m_storage.firstFreeChunkIdx = chunkThatRemovedEntity.
idx();
757 auto* pChunk = m_storage.chunks[m_storage.firstFreeChunkIdx];
758 if (pChunk->size() >= pChunk->capacity())
759 ++m_storage.firstFreeChunkIdx;
768 try_update_free_chunk_idx(chunk);
776 remove_entity_raw(chunk, row, recs);
783 return m_shape.properties;
789 return m_storage.chunks;
795 return m_shape.hashLookup;
801 return {&m_shape.ids[0], m_shape.properties.cntEntities};
807 return {&m_shape.compOffs[0], m_shape.properties.cntEntities};
812 GAIA_NODISCARD uint32_t
pairs()
const {
813 return m_pPairIndex !=
nullptr ? m_pPairIndex->pairCnt : 0;
819 return m_pPairIndex !=
nullptr ? m_pPairIndex->pairCntIs : 0;
827 return m_pPairIndex !=
nullptr ? m_pPairIndex->pair_matches(ids_view(),
pair) : 0;
834 GAIA_ASSERT(m_pPairIndex !=
nullptr);
835 return m_pPairIndex->entity_from_pairs_as_idx(ids_view(), idx);
841 return m_pPairIndex !=
nullptr ? m_pPairIndex->pair_indices() : std::span<const uint8_t>{};
848 return m_pPairIndex !=
nullptr ? m_pPairIndex->pair_rel_indices(relation) : std::span<const uint8_t>{};
855 return m_pPairIndex !=
nullptr ? m_pPairIndex->pair_tgt_indices(target) : std::span<const uint8_t>{};
862 return core::has_if(ids_view(), [&](
Entity e) {
869 GAIA_ASSERT(m_runtime.observedTermCnt < m_shape.properties.cntEntities);
870 ++m_runtime.observedTermCnt;
875 GAIA_ASSERT(m_runtime.observedTermCnt > 0);
876 --m_runtime.observedTermCnt;
882 return m_runtime.observedTermCnt != 0;
888 template <
typename T>
889 GAIA_NODISCARD
bool has()
const {
891 const auto rel = m_cc.get<
typename T::rel>().entity;
892 const auto tgt = m_cc.get<
typename T::tgt>().entity;
895 const auto* pComp = m_cc.find<T>();
896 return pComp !=
nullptr && has(pComp->entity);
907 template <
bool Enabled>
910 for (
const auto* pChunk: chunks()) {
915 if constexpr (Enabled) {
916 cnt = pChunk->size_enabled();
918 cnt = pChunk->size_disabled();
921 if (flatIdx < offset + cnt) {
922 if constexpr (Enabled) {
923 const auto idx = (uint32_t)(flatIdx - offset) + pChunk->size_disabled();
924 return pChunk->entity_view()[idx];
926 const auto idx = (uint32_t)(flatIdx - offset);
927 return pChunk->entity_view()[idx];
945 template <
bool Enabled>
948 for (
const auto* pChunk: chunks()) {
953 if constexpr (Enabled) {
954 cnt = pChunk->size_enabled();
956 cnt = pChunk->size_disabled();
959 if (flatIdx < offset + cnt) {
960 if constexpr (Enabled) {
961 const auto idx = (uint32_t)(flatIdx - offset) + pChunk->size_disabled();
962 const auto* pData = pChunk->comp_ptr(compIdx, idx);
963 outEntity = pChunk->entity_view()[idx];
966 const auto idx = (uint32_t)(flatIdx - offset);
967 const auto* pData = pChunk->comp_ptr(compIdx, idx);
968 outEntity = pChunk->entity_view()[idx];
981 template <
bool Enabled>
984 const auto pivotIdx = low + (high - low) / 2;
985 Entity pivotEntity = get_flat_entity<Enabled>(pivotIdx);
986 if (pivotIdx != high) {
987 const Entity highEntity = get_flat_entity<Enabled>(high);
988 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), pivotEntity, highEntity);
992 for (
size_t j = low; j < high; ++j) {
993 Entity jEntity = get_flat_entity<Enabled>(j);
994 if (func(m_world, &jEntity, &pivotEntity) < 0) {
996 Entity iEntity = get_flat_entity<Enabled>(i);
997 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
1004 Entity iEntity = get_flat_entity<Enabled>(i);
1005 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
1010 if (i - low < high - i) {
1012 sort_entities_inter<Enabled>(low, i - 1, func);
1016 sort_entities_inter<Enabled>(i + 1, high, func);
1025 template <
bool Enabled>
1027 const ComponentCacheItem* pItem, uint32_t compIdx,
size_t low,
size_t high, TSortByFunc func) {
1028 while (low < high) {
1029 const auto pivotIdx = low + (high - low) / 2;
1031 (void)get_flat_comp_ptr<Enabled>(compIdx, pivotIdx, pivotEntity);
1032 if (pivotIdx != high) {
1034 (void)get_flat_comp_ptr<Enabled>(compIdx, high, highEntity);
1035 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), pivotEntity, highEntity);
1037 const void* pPivotData = get_flat_comp_ptr<Enabled>(compIdx, high, pivotEntity);
1040 for (
size_t j = low; j < high; ++j) {
1042 const void* jData = get_flat_comp_ptr<Enabled>(compIdx, j, jEntity);
1043 if (func(m_world, jData, pPivotData) < 0) {
1046 (void)get_flat_comp_ptr<Enabled>(compIdx, i, iEntity);
1047 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, jEntity);
1055 (void)get_flat_comp_ptr<Enabled>(compIdx, i, iEntity);
1056 Chunk::swap_chunk_entities(
const_cast<World&
>(m_world), iEntity, pivotEntity);
1059 if (i - low < high - i) {
1061 sort_entities_inter<Enabled>(pItem, compIdx, low, i - 1, func);
1065 sort_entities_inter<Enabled>(pItem, compIdx, i + 1, high, func);
1083 if (entity == EntityBad) {
1085 uint32_t entities = 0;
1086 for (
const auto* pChunk: m_storage.chunks)
1087 entities += pChunk->size_enabled();
1089 sort_entities_inter<true>(0, entities - 1, func);
1092 uint32_t entities = 0;
1093 for (
const auto* pChunk: m_storage.chunks)
1094 entities += pChunk->size_disabled();
1096 sort_entities_inter<false>(0, entities - 1, func);
1099 const auto* pItem = m_cc.find(entity);
1100 GAIA_ASSERT(pItem !=
nullptr &&
"Trying to sort by a component that has not been registered");
1101 if (pItem ==
nullptr)
1104 const auto compIdx = chunks()[0]->comp_idx(entity);
1106 uint32_t entities = 0;
1107 for (
const auto* pChunk: m_storage.chunks)
1108 entities += pChunk->size_enabled();
1110 sort_entities_inter<true>(pItem, compIdx, 0, entities - 1, func);
1113 uint32_t entities = 0;
1114 for (
const auto* pChunk: m_storage.chunks)
1115 entities += pChunk->size_disabled();
1117 sort_entities_inter<false>(pItem, compIdx, 0, entities - 1, func);
1129 GAIA_ASSERT(pArchetypeRight !=
this);
1131 m_edges.graph.add_edge_right(entity, pArchetypeRight->
id(), pArchetypeRight->
id_hash());
1140 GAIA_ASSERT(pArchetypeLeft !=
this);
1142 m_edges.graph.add_edge_left(entity, pArchetypeLeft->
id(), pArchetypeLeft->
id_hash());
1150 GAIA_ASSERT(pArchetypeRight !=
this);
1152 m_edges.graph.del_edge_right(entity);
1161 GAIA_ASSERT(pArchetypeLeft !=
this);
1163 m_edges.graph.del_edge_left(entity);
1170 m_edges.graph.del_edge_right(entity);
1177 m_edges.graph.del_edge_left(entity);
1184 return m_edges.graph.find_edge_right(entity);
1191 return m_edges.graph.find_edge_left(entity);
1197 return m_edges.graph.right_edges();
1203 return m_edges.graph.right_edges();
1209 return m_edges.graph.left_edges();
1215 return m_edges.graph.left_edges();
1221 return m_storage.chunks.empty();
1226 m_runtime.deleteReq = 1;
1232 return m_runtime.deleteReq;
1240 GAIA_ASSERT(lifespan <= MAX_ARCHETYPE_LIFESPAN);
1242 m_runtime.lifespanCountdownMax = lifespan;
1243 if (lifespan != 0 && m_runtime.lifespanCountdown > lifespan)
1244 m_runtime.lifespanCountdown = lifespan;
1251 return m_runtime.lifespanCountdownMax;
1257 return m_runtime.lifespanCountdown > 0;
1268 return m_runtime.dead == 1;
1273 GAIA_ASSERT(!dead());
1274 m_runtime.lifespanCountdown = m_runtime.lifespanCountdownMax;
1279 GAIA_ASSERT(!dead());
1280 m_runtime.lifespanCountdown = 0;
1281 m_runtime.deleteReq = 0;
1287 GAIA_ASSERT(dying());
1288 GAIA_ASSERT(m_runtime.lifespanCountdownMax > 0);
1289 --m_runtime.lifespanCountdown;
1296 return m_runtime.lifespanCountdownMax > 0 && !dying() && empty();
1304 const auto name = entity_name(world, entity);
1306 " ent [%u:%u] %.*s", entity.
id(), entity.
gen(), (
int)name.size(), name.empty() ?
"" : name.data());
1307 }
else if (entity.
pair()) {
1308 const auto rel = entity_name(world, entity.
id());
1309 const auto tgt = entity_name(world, entity.
gen());
1311 " pair [%u:%u] %.*s -> %.*s", entity.
id(), entity.
gen(), (
int)rel.size(),
1312 rel.empty() ?
"" : rel.data(), (int)tgt.size(), tgt.empty() ?
"" : tgt.data());
1314 const auto& cc = comp_cache(world);
1315 const auto& desc = cc.get(entity);
1316 const auto symbol = desc.symbol_name();
1318 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %.*s", desc.hashLookup.hash, desc.comp.size(),
1319 desc.comp.alig(), desc.entity.id(), desc.entity.gen(), (
int)symbol.size(), symbol.data());
1330 uint32_t entCnt = 0;
1331 uint32_t entCntDisabled = 0;
1332 for (
const auto* chunk: archetype.m_storage.chunks) {
1333 entCnt += chunk->size();
1334 entCntDisabled += chunk->size_disabled();
1338 uint32_t compsSize = 0;
1340 const auto& p = archetype.
props();
1341 GAIA_FOR(p.cntEntities)
1342 compsSize += comp_from_item(ids[i], archetype.m_shape.compItems[i]).size();
1346 const auto sizeType = mem_block_size_type(chunkBytes);
1347 const auto allocSize = mem_block_size(sizeType) / 1024;
1351 "hash:%016" PRIx64
", "
1352 "chunks:%u (%uK), data:%u/%u B, "
1353 "entities:%u/%u/%u",
1358 GAIA_LOG_N(
" Components - count:%u", (uint32_t)ids.size());
1359 for (
const auto ent: ids)
1360 diag_entity(world, ent);
1368 archetype.m_edges.graph.diag(world);
1374 const auto& chunks = archetype.m_storage.chunks;
1378 GAIA_LOG_N(
" Chunks");
1379 for (
const auto* pChunk: chunks)
1387 const auto& chunks = archetype.m_storage.chunks;
1391 GAIA_LOG_N(
" Entities");
1392 bool noEntities =
true;
1393 for (
const auto* pChunk: chunks) {
1394 if (pChunk->empty())
1398 auto ev = pChunk->entity_view();
1399 for (
auto entity: ev)
1400 diag_entity(world, entity);
1410 diag_basic_info(world, archetype);
1411 diag_graph_info(world, archetype);
1412 diag_chunk_info(archetype);
1413 diag_entity_info(world, archetype);
1425 static constexpr bool IsDirectHashKey =
true;
1428 explicit ArchetypeLookupKey(Archetype::LookupHash hash,
const ArchetypeBase* pArchetypeBase):
1429 m_hash(hash), m_pArchetypeBase(pArchetypeBase) {}
1433 GAIA_NODISCARD
size_t hash()
const {
1434 return (
size_t)m_hash.hash;
1446 if GAIA_LIKELY (m_hash != other.m_hash)
1449 const auto
id = m_pArchetypeBase->
id();
1450 if (
id == ArchetypeIdBad) {
1451 const auto* pArchetype = (
const Archetype*)other.m_pArchetypeBase;
1453 return pArchetype->cmp_comps(*pArchetypeLookupChecker);
1460 return m_pArchetypeBase == other.m_pArchetypeBase;
1464 using ArchetypeMapByHash = cnt::map<ArchetypeLookupKey, Archetype*>;
Array of elements of type.
Definition darray_ext_impl.h:31
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_ext_impl.h:561
Base shared by archetype lookups and concrete archetypes, carrying the archetype id.
Definition archetype.h:64
ArchetypeId m_archetypeId
Archetype ID - used to address the archetype directly in the world's list or archetypes.
Definition archetype.h:67
GAIA_NODISCARD ArchetypeId id() const
Archetype id used to address the archetype in the world list.
Definition archetype.h:72
Archetype lookup key that compares against a component term span instead of a full archetype id.
Definition archetype.h:78
GAIA_NODISCARD bool cmp_comps(const ArchetypeLookupChecker &other) const
Compares the lookup checker component spans in archetype order.
Definition archetype.h:90
Hashmap key for archetype lookup, combining a hash with the archetype base pointer....
Definition archetype.h:1419
GAIA_NODISCARD size_t hash() const
Hash of the archetype lookup value.
Definition archetype.h:1433
GAIA_NODISCARD Archetype * archetype() const
Resolves the lookup key to its concrete archetype.
Definition archetype.h:1439
Fixed-shape group of chunks storing entities that share the same component layout....
Definition archetype.h:97
Entity get_flat_entity(size_t flatIdx) const
Given a flat index, returns an entity at that index. E.g., if there are 2 chunks, the first one with ...
Definition archetype.h:908
void observed_terms_dec()
Decrements the number of terms observing this archetype.
Definition archetype.h:874
QueryMask queryMask() const
Query mask describing which simple queries may match this archetype.
Definition archetype.h:648
static GAIA_NODISCARD Archetype * create(const World &world, ArchetypeId archetypeId, uint32_t &worldVersion, EntitySpan ids)
Creates a new archetype from a component term span.
Definition archetype.h:539
GAIA_NODISCARD Chunk * foc_free_chunk()
Tries to locate a chunk that has some space left for a new entity. If not found a new chunk is create...
Definition archetype.h:700
void set_max_lifespan(uint32_t lifespan)
Sets maximal lifespan of an archetype. A dying archetype keeps no more of its countdown than the new ...
Definition archetype.h:1239
void build_graph_edges(Archetype *pArchetypeRight, Entity entity)
Builds a graph edge from this archetype to the right archetype.
Definition archetype.h:1127
GAIA_NODISCARD bool cmp_comps(const ArchetypeLookupChecker &other) const
Compares the lookup checker component spans in archetype order.
Definition archetype.h:528
void sort_entities_inter(const ComponentCacheItem *pItem, uint32_t compIdx, size_t low, size_t high, TSortByFunc func)
Generic in-place quicksort across chunks.
Definition archetype.h:1026
GAIA_NODISCARD EntitySpan ids_view() const
Span over the component and entity identifiers defining the archetype shape.
Definition archetype.h:800
void set_hashes(LookupHash hashLookup)
Sets hashes for each component type and lookup.
Definition archetype.h:660
void observed_terms_inc()
Increments the number of terms observing this archetype.
Definition archetype.h:868
GAIA_NODISCARD const auto & left_edges() const
Const view over the archetype "del" graph edges.
Definition archetype.h:1214
static void diag_basic_info(const World &world, const Archetype &archetype)
Logs basic archetype diagnostics: sizes, chunk count, entity counts, and component ids.
Definition archetype.h:1326
static void destroy(Archetype *pArchetype)
Destroys the archetype and frees its memory.
Definition archetype.h:640
GAIA_NODISCARD auto & left_edges()
Mutable view over the archetype "del" graph edges.
Definition archetype.h:1208
GAIA_NODISCARD uint32_t pairs() const
Returns the number of pairs registered in the archetype.
Definition archetype.h:812
void del_graph_edge_left_local(Entity entity)
Deletes a cached local "del" edge formed by entity. Intended for stale edge cache recovery when the o...
Definition archetype.h:1176
void remove_entity(Chunk &chunk, uint16_t row, EntityContainers &recs)
Removes an entity from the chunk and updates the chunk versions.
Definition archetype.h:775
void del(Chunk *pChunk)
Removes a chunk from the list of chunks managed by their archetype and deletes its memory.
Definition archetype.h:676
GAIA_NODISCARD uint32_t pairs_is() const
Returns the number of Is pairs registered in the archetype.
Definition archetype.h:818
void sort_entities_inter(size_t low, size_t high, TSortByFunc func)
Generic in-place quicksort across chunks.
Definition archetype.h:982
GAIA_NODISCARD bool ready_to_die() const
Tells whether archetype is ready to be deleted.
Definition archetype.h:1295
GAIA_NODISCARD bool dead() const
Checks is this chunk is dying.
Definition archetype.h:1267
GAIA_NODISCARD bool has_observed_terms() const
Whether any observer term currently observes this archetype.
Definition archetype.h:881
ArchetypeIdLookupKey::LookupHash id_hash() const
Archetype id hash used for graph edge storage.
Definition archetype.h:654
void del_graph_edges(Archetype *pArchetypeRight, Entity entity)
Removes the cached "add" edge for entity and propagates the delete to the right archetype.
Definition archetype.h:1148
const void * get_flat_comp_ptr(uint32_t compIdx, size_t flatIdx, Entity &outEntity) const
Given a flat index, returns pointer to component data at that index. E.g., if there are 2 chunks,...
Definition archetype.h:946
GAIA_NODISCARD const Properties & props() const
Shape properties of this archetype.
Definition archetype.h:782
void enable_entity(Chunk *pChunk, uint16_t row, bool enableEntity, EntityContainers &recs)
Enables or disables the entity on a given row in the chunk.
Definition archetype.h:669
void die()
Marks the chunk as dead.
Definition archetype.h:1261
GAIA_NODISCARD ArchetypeGraphEdge find_edge_left(Entity entity) const
Checks if an archetype graph "del" edge with entity entity exists.
Definition archetype.h:1190
static void diag(const World &world, const Archetype &archetype)
Performs diagnostics on a specific archetype. Prints basic info about it and the chunks it contains.
Definition archetype.h:1409
GAIA_NODISCARD bool progress_death()
Updates internal lifespan.
Definition archetype.h:1286
GAIA_NODISCARD LookupHash lookup_hash() const
Hash of the component terms used for archetype lookup.
Definition archetype.h:794
GAIA_NODISCARD bool empty() const
Checks is there are no chunk in the archetype.
Definition archetype.h:1220
void save(ser::serializer &s)
Serializes the archetype: free-chunk index, list index, and each chunk.
Definition archetype.h:472
GAIA_NODISCARD bool has(Entity entity) const
Checks if an entity is a part of the archetype.
Definition archetype.h:861
GAIA_NODISCARD const auto & right_edges() const
Const view over the archetype "add" graph edges.
Definition archetype.h:1202
GAIA_NODISCARD bool is_req_del() const
Returns true if this archetype is requested to be deleted.
Definition archetype.h:1231
void load(ser::serializer &s)
Deserializes the archetype, recreating missing chunks from the shape description.
Definition archetype.h:485
static void diag_entity(const World &world, Entity entity)
Logs a diagnostic line for one entity, pair, or component of the archetype.
Definition archetype.h:1302
void del_graph_edges_left(Archetype *pArchetypeLeft, Entity entity)
Removes the cached "del" edge for entity after the matching "add" edge is removed.
Definition archetype.h:1159
GAIA_NODISCARD bool dying() const
Checks is this chunk is dying.
Definition archetype.h:1256
void list_idx(uint32_t idx)
Sets the archetype index in the world list of active archetypes.
Definition archetype.h:515
GAIA_NODISCARD auto & right_edges()
Mutable view over the archetype "add" graph edges.
Definition archetype.h:1196
GAIA_NODISCARD bool has() const
Checks if component T is present in the chunk.
Definition archetype.h:889
GAIA_NODISCARD ChunkDataOffsetSpan comp_offs_view() const
Span over the per-term component data offsets.
Definition archetype.h:806
void build_graph_edges_left(Archetype *pArchetypeLeft, Entity entity)
Records a cached "del" graph edge from entity entity to this archetype.
Definition archetype.h:1138
void revive()
Makes the archetype alive again.
Definition archetype.h:1278
void try_update_free_chunk_idx()
Tries to update the index of the first chunk that has space left for at least one entity.
Definition archetype.h:733
GAIA_NODISCARD ArchetypeGraphEdge find_edge_right(Entity entity) const
Checks if an archetype graph "add" edge with entity entity exists.
Definition archetype.h:1183
void req_del()
Request deleting the archetype.
Definition archetype.h:1225
uint32_t list_idx() const
Archetype index in the world list of active archetypes.
Definition archetype.h:521
void try_update_free_chunk_idx(Chunk &chunkThatRemovedEntity)
Tries to update the index of the first chunk that has space left for at least one entity.
Definition archetype.h:745
GAIA_NODISCARD std::span< const uint8_t > pair_tgt_indices(Entity target) const
Indices of pairs whose target matches the given target.
Definition archetype.h:854
GAIA_NODISCARD Entity entity_from_pairs_as_idx(uint32_t idx) const
Resolves a relation-pair index to its pair entity.
Definition archetype.h:833
static void diag_entity_info(const World &world, const Archetype &archetype)
Logs entity diagnostics for each entity of the archetype.
Definition archetype.h:1386
GAIA_NODISCARD uint32_t pair_matches(Entity pair) const
Returns how many pair ids in this archetype match the provided wildcard-capable pair query....
Definition archetype.h:826
void remove_entity_raw(Chunk &chunk, uint16_t row, EntityContainers &recs)
Removes an entity from the chunk.
Definition archetype.h:766
static void diag_graph_info(const World &world, const Archetype &archetype)
Logs the archetype graph edge diagnostics.
Definition archetype.h:1367
GAIA_NODISCARD std::span< const uint8_t > pair_rel_indices(Entity relation) const
Indices of pairs whose relation matches the given relation.
Definition archetype.h:847
GAIA_NODISCARD std::span< const uint8_t > pair_indices() const
Indices of all relation pairs within the ids array.
Definition archetype.h:840
void start_dying()
Starts the process of dying.
Definition archetype.h:1272
void del_graph_edge_right_local(Entity entity)
Deletes a cached local "add" edge formed by entity. Intended for stale edge cache recovery when the o...
Definition archetype.h:1169
GAIA_NODISCARD const cnt::darray< Chunk * > & chunks() const
Chunks managed by this archetype.
Definition archetype.h:788
GAIA_NODISCARD uint32_t max_lifespan() const
Returns the maximal lifespan of the archetype. If zero, the archetype it kept indefinitely.
Definition archetype.h:1250
static void diag_chunk_info(const Archetype &archetype)
Logs per-chunk diagnostics for all chunks of the archetype.
Definition archetype.h:1373
void sort_entities(Entity entity, TSortByFunc func)
Sorts all entities in the archetypes according to the given function.
Definition archetype.h:1076
Fixed-capacity archetype storage unit holding entities and their component columns.
Definition chunk.h:36
void remove_entity(uint16_t row, EntityContainers &recs)
Tries to remove the entity at row row. Removal is done via swapping with last entity in chunk....
Definition chunk.h:1212
GAIA_NODISCARD uint32_t idx() const
Returns the index of this chunk in its archetype's storage.
Definition chunk.h:1645
void update_versions()
Updates the version numbers for this chunk.
Definition chunk.h:573
GAIA_NODISCARD uint16_t size() const
Returns the total number of entities in the chunk (both enabled and disabled)
Definition chunk.h:1738
void enable_entity(uint16_t row, bool enableEntity, EntityContainers &recs)
Enables or disables the entity on a given row in the chunk.
Definition chunk.h:1338
Owns entities, components, archetypes, queries, observers, and systems.
Definition world.h:80
Wrapper for two Entities forming a relationship pair. Conversion to Entity stores only id() of each e...
Definition id.h:559
Wrapper for two types forming a relationship pair. Depending on what types are used to form a pair it...
Definition id.h:197
T hash
Precomputed hash value.
Definition hashing_policy.h:51
Pairs an archetype with one of its chunks.
Definition archetype.h:52
Archetype * pArchetype
Archetype the chunk belongs to.
Definition archetype.h:54
Chunk * pChunk
Chunk owned by the paired archetype.
Definition archetype.h:56
Counts how many pair indices of one relation or target occupy a contiguous range.
Definition archetype.h:177
EntityId id
Relation or target entity id the bucket counts.
Definition archetype.h:179
uint8_t count
Number of pair indices belonging to this relation or target.
Definition archetype.h:183
uint8_t start
First pair index within the archetype ids array.
Definition archetype.h:181
Shape properties of this archetype.
Definition archetype.h:110
uint16_t capacity
The number of data entities this archetype can take (e.g 5 = 5 entities with all their components)
Definition archetype.h:112
uint8_t cntEntities
Total number of entities/components.
Definition archetype.h:116
ChunkDataOffset chunkDataBytes
How many bytes of data is needed for a fully utilized chunk.
Definition archetype.h:114
Identifier of a registered component type. Packs the component id, size, alignment,...
Definition id.h:45
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:231
GAIA_NODISCARD constexpr auto gen() const noexcept
Generation index of the entity.
Definition id.h:301
GAIA_NODISCARD constexpr bool pair() const noexcept
Whether this id refers to a relationship pair.
Definition id.h:313
GAIA_NODISCARD constexpr auto id() const noexcept
Entity index in the entity array.
Definition id.h:295
GAIA_NODISCARD constexpr bool entity() const noexcept
Whether this id refers to an entity.
Definition id.h:307
Detects whether a type is a relationship pair.
Definition id.h:220
Runtime serializer type-erased handle. Traversal logic is shared with compile-time serialization,...
Definition ser_rt.h:94
void load(T &arg)
Deserializes a value through generic traversal.
Definition ser_rt.h:126
void save(const T &arg)
Serializes a value through generic traversal.
Definition ser_rt.h:115