diff --git a/include/UECS/Chunk.hpp b/include/UECS/Chunk.hpp index 651636b..74107a8 100644 --- a/include/UECS/Chunk.hpp +++ b/include/UECS/Chunk.hpp @@ -1,13 +1,10 @@ #pragma once #include "Entity.hpp" - +#include "CmptPtr.hpp" #include "config.hpp" -#include - #include -#include namespace Ubpa::UECS { class Archetype; @@ -38,8 +35,13 @@ namespace Ubpa::UECS { bool HasAnyChange(std::span types, std::uint64_t version) const noexcept; + void ApplyChanges(std::span types); + + // ApplyChanges + std::tuple, small_vector> Locate(std::span types); + private: - friend Archetype; + friend class Archetype; struct Head { Archetype* archetype; @@ -69,6 +71,8 @@ namespace Ubpa::UECS { Head* GetHead() noexcept { return reinterpret_cast(data); } const Head* GetHead() const noexcept { return reinterpret_cast(data); } + std::size_t Erase(std::size_t idx); + static_assert(ChunkSize > sizeof(Head)); std::uint8_t data[ChunkSize]; }; diff --git a/include/UECS/EntityMngr.hpp b/include/UECS/EntityMngr.hpp index 9357603..cfc4a0f 100644 --- a/include/UECS/EntityMngr.hpp +++ b/include/UECS/EntityMngr.hpp @@ -114,7 +114,8 @@ namespace Ubpa::UECS { struct EntityInfo { Archetype* archetype{ nullptr }; - std::size_t idxInArchetype{ static_cast(-1) }; + std::size_t chunkIdx{ static_cast(-1) }; + std::size_t idxInChunk{ static_cast(-1) }; std::uint64_t version{ 0 }; // version }; std::vector entityTable; diff --git a/src/core/Archetype.cpp b/src/core/Archetype.cpp index afa6ee8..1a27606 100644 --- a/src/core/Archetype.cpp +++ b/src/core/Archetype.cpp @@ -17,7 +17,7 @@ Archetype::~Archetype() { if (!trait.dtor) continue; for (std::size_t k = 0; k < chunks.size(); k++) { - std::size_t num = EntityNumOfChunk(k); + std::size_t num = chunks[k]->EntityNum(); std::uint8_t* buffer = chunks[k]->data; std::uint8_t* beg = buffer + offsets[i]; for (std::size_t i = 0; i < num; i++) { @@ -36,42 +36,28 @@ Archetype::Archetype(std::pmr::memory_resource* rsrc, std::pmr::memory_resource* world_rsrc{ world_rsrc } { cmptTraits = src.cmptTraits; - entityNum = src.entityNum; chunkCapacity = src.chunkCapacity; offsets = src.offsets; version = src.version; + entityNum = src.entityNum; + nonFullChunks = src.nonFullChunks; chunks.resize(src.chunks.size(), nullptr); if (cmptTraits.IsTrivial()) { - // [0, src.chunks.size() - 1) - for (std::size_t i = 0; i < src.chunks.size() - 1; i++) { + for (std::size_t i = 0; i < src.chunks.size(); i++) { chunks[i] = chunkAllocator.allocate(1); auto* srcChunk = src.chunks[i]; auto* dstChunk = chunks[i]; std::memcpy(dstChunk->data, srcChunk->data, sizeof(Chunk)); dstChunk->GetHead()->UpdateVersion(version); dstChunk->GetHead()->archetype = this; - } - { // src.chunks.size() - 1 - auto* srcChunk = src.chunks.back(); - auto* dstChunk = chunks.back() = chunkAllocator.allocate(1); - std::memcpy(dstChunk, srcChunk, sizeof(Chunk::Head) + sizeof(Chunk::Head::CmptInfo) * cmptTraits.GetTypes().size()); - dstChunk->GetHead()->UpdateVersion(version); - dstChunk->GetHead()->archetype = this; - std::size_t num = src.EntityNumOfChunk(src.chunks.size() - 1); - for (std::size_t i = 0; i < cmptTraits.GetTraits().size(); i++) { - const auto& trait = cmptTraits.GetTraits()[i]; - auto* srcBegin = srcChunk->data + offsets[i]; - auto* dstBegin = dstChunk->data + offsets[i]; - if (trait.copy_ctor) { - for (std::size_t j = 0; j < num; j++) { - auto offset_j = j * trait.size; - trait.copy_ctor(dstBegin + offset_j, srcBegin + offset_j, world_rsrc); - } - } - else - memcpy(dstBegin, srcBegin, num * trait.size); + std::size_t num = srcChunk->EntityNum(); + for (std::size_t j = 0; j < cmptTraits.GetTraits().size(); j++) { + const auto& trait = cmptTraits.GetTraits()[j]; + auto* srcBegin = srcChunk->data + offsets[j]; + auto* dstBegin = dstChunk->data + offsets[j]; + memcpy(dstBegin, srcBegin, num * trait.size); } } } @@ -82,7 +68,7 @@ Archetype::Archetype(std::pmr::memory_resource* rsrc, std::pmr::memory_resource* std::memcpy(dstChunk, srcChunk, sizeof(Chunk::Head) + sizeof(Chunk::Head::CmptInfo) * cmptTraits.GetTypes().size()); dstChunk->GetHead()->UpdateVersion(version); dstChunk->GetHead()->archetype = this; - std::size_t num = src.EntityNumOfChunk(i); + std::size_t num = srcChunk->EntityNum(); for (std::size_t j = 0; j < cmptTraits.GetTraits().size(); j++) { const auto& trait = cmptTraits.GetTraits()[j]; auto* cursor_src = srcChunk->data + offsets[j]; @@ -186,10 +172,10 @@ Archetype* Archetype::Remove(const Archetype* from, std::span type return rst; } -std::size_t Archetype::Create(Entity e) { - std::size_t idx = RequestBuffer(); - std::size_t idxInChunk = idx % chunkCapacity; - Chunk* chunk = chunks[idx / chunkCapacity]; +Archetype::EntityAddress Archetype::Create(Entity e) { + EntityAddress addr = RequestBuffer(); + std::size_t idxInChunk = addr.idxInChunk; + Chunk* chunk = chunks[addr.chunkIdx]; std::uint8_t* buffer = chunk->data; for (std::size_t i = 0; i < cmptTraits.GetTraits().size(); i++) { @@ -207,11 +193,13 @@ std::size_t Archetype::Create(Entity e) { } chunk->GetHead()->UpdateVersion(version); - return idx; + entityNum++; + + return addr; } -std::size_t Archetype::RequestBuffer() { - if (entityNum == chunks.size() * chunkCapacity) { +Archetype::EntityAddress Archetype::RequestBuffer() { + if (nonFullChunks.empty()) { auto* chunk = chunkAllocator.allocate(1); // init chunk @@ -225,13 +213,23 @@ std::size_t Archetype::RequestBuffer() { info.offset = offsets[i]; } chunk->GetHead()->UpdateVersion(version); + chunks.push_back(chunk); + + nonFullChunks.insert(chunks.size() - 1); } - return entityNum++; + + std::size_t chunkIdx = nonFullChunks.back(); + Chunk::Head* chunkhead = chunks[chunkIdx]->GetHead(); + std::size_t idxInChunk = chunkhead->num_entity++; + + if (chunkhead->num_entity == chunkhead->capacity) + nonFullChunks.erase(chunkIdx); + + return EntityAddress{ .chunkIdx = chunkIdx, .idxInChunk = idxInChunk }; } -CmptAccessPtr Archetype::At(AccessTypeID type, std::size_t idx) const { - assert(idx < entityNum); +CmptAccessPtr Archetype::At(AccessTypeID type, EntityAddress addr) const { auto target = cmptTraits.GetTypes().find(type); if (target == cmptTraits.GetTypes().end()) return nullptr; @@ -240,8 +238,8 @@ CmptAccessPtr Archetype::At(AccessTypeID type, std::size_t idx) const { const auto& trait = cmptTraits.GetTraits()[typeIdx]; std::size_t size = trait.size; std::size_t offset = offsets[typeIdx]; - std::size_t idxInChunk = idx % chunkCapacity; - Chunk* chunk = chunks[idx / chunkCapacity]; + std::size_t idxInChunk = addr.idxInChunk; + Chunk* chunk = chunks[addr.chunkIdx]; std::uint8_t* buffer = chunk->data; if(type.GetAccessMode() == AccessMode::WRITE) chunk->GetHead()->GetCmptInfos()[typeIdx].version = version; @@ -249,37 +247,40 @@ CmptAccessPtr Archetype::At(AccessTypeID type, std::size_t idx) const { return { type, buffer + offset + idxInChunk * size }; } -std::size_t Archetype::Instantiate(Entity e, std::size_t srcIdx) { - assert(srcIdx < entityNum); +Archetype::EntityAddress Archetype::Instantiate(Entity e, EntityAddress src) { + EntityAddress dst; - std::size_t dstIdx = RequestBuffer(); + Chunk* srcChunk = chunks[src.chunkIdx]; + if (!srcChunk->Full()) { + dst = { .chunkIdx = src.chunkIdx, .idxInChunk = srcChunk->GetHead()->num_entity++ }; + if (srcChunk->Full()) + nonFullChunks.erase(src.chunkIdx); + } + else + dst = RequestBuffer(); - std::size_t srcIdxInChunk = srcIdx % chunkCapacity; - std::size_t dstIdxInChunk = dstIdx % chunkCapacity; - std::uint8_t* srcBuffer = chunks[srcIdx / chunkCapacity]->data; - Chunk* dstChunk = chunks[dstIdx / chunkCapacity]; + std::size_t srcIdxInChunk = src.idxInChunk; + std::size_t dstIdxInChunk = dst.idxInChunk; + std::uint8_t* srcBuffer = srcChunk->data; + Chunk* dstChunk = chunks[dst.chunkIdx]; std::uint8_t* dstBuffer = dstChunk->data; for (std::size_t i = 0; i < cmptTraits.GetTypes().size(); i++) { const auto& trait = cmptTraits.GetTraits()[i]; std::size_t offset = offsets[i]; - if (cmptTraits.GetTypes().data()[i].Is()) { - constexpr std::size_t size = sizeof(Entity); - memcpy(dstBuffer + offset + dstIdxInChunk * size, &e, size); - } - else { - std::size_t size = trait.size; - std::uint8_t* dst = dstBuffer + offset + dstIdxInChunk * size; - std::uint8_t* src = srcBuffer + offset + srcIdxInChunk * size; + std::size_t size = trait.size; + std::uint8_t* dst = dstBuffer + offset + dstIdxInChunk * size; + std::uint8_t* src = srcBuffer + offset + srcIdxInChunk * size; - trait.CopyConstruct(dst, src, world_rsrc); - } + trait.CopyConstruct(dst, src, world_rsrc); } dstChunk->GetHead()->UpdateVersion(version); - return dstIdx; + entityNum++; + + return dst; } std::tuple< @@ -320,82 +321,31 @@ Archetype::Locate(std::span cmpts) const { return { chunkEntity, chunkCmpts, sizes }; } -std::size_t Archetype::Erase(std::size_t idx) { - assert(idx < entityNum); - - std::size_t dstIdxInChunk = idx % chunkCapacity; - Chunk* dstChunk = chunks[idx / chunkCapacity]; - std::uint8_t* dstBuffer = dstChunk->data; - - std::size_t movedIdx = static_cast(-1); - - if (idx != entityNum - 1) { - std::size_t movedIdxInArchetype = entityNum - 1; - - std::size_t srcIdxInChunk = movedIdxInArchetype % chunkCapacity; - Chunk* srcChunk = chunks[movedIdxInArchetype / chunkCapacity]; - std::uint8_t* srcBuffer = srcChunk->data; - - for (std::size_t i = 0; i < cmptTraits.GetTypes().size(); i++) { - const auto& trait = cmptTraits.GetTraits()[i]; - std::size_t size = trait.size; - std::size_t offset = offsets[i]; - std::uint8_t* dst = dstBuffer + offset + dstIdxInChunk * size; - std::uint8_t* src = srcBuffer + offset + srcIdxInChunk * size; +std::size_t Archetype::Erase(EntityAddress addr) { + Chunk* chunk = chunks[addr.chunkIdx]; - if (cmptTraits.GetTypes().data()[i].Is()) - movedIdx = reinterpret_cast(src)->index; + std::size_t movedEntityIdx = chunk->Erase(addr.idxInChunk); - trait.MoveAssign(dst, src); - trait.Destruct(src); - } - - srcChunk->GetHead()->UpdateVersion(version); - } - else if(!cmptTraits.IsTrivial()) { - for (std::size_t i = 0; i < cmptTraits.GetTypes().size(); i++) { - const auto& trait = cmptTraits.GetTraits()[i]; - std::size_t size = trait.size; - std::size_t offset = offsets[i]; - std::uint8_t* dst = dstBuffer + offset + dstIdxInChunk * size; - trait.Destruct(dst); - } - } - - dstChunk->GetHead()->UpdateVersion(version); + if (chunk->Full()) + nonFullChunks.erase(addr.chunkIdx); entityNum--; - if (chunks.size() * chunkCapacity - entityNum >= chunkCapacity) { - Chunk* chunk = chunks.back(); - chunkAllocator.deallocate(chunk, 1); - chunks.pop_back(); - } - - return movedIdx; + return movedEntityIdx; } -vector Archetype::Components(std::size_t idx, AccessMode mode) const { +vector Archetype::Components(EntityAddress addr, AccessMode mode) const { vector rst; for (const auto& type : cmptTraits.GetTypes()) { if (type.Is()) continue; - rst.push_back(At({ type,mode }, idx)); + rst.push_back(At({ type,mode }, addr)); } return rst; } -std::size_t Archetype::EntityNumOfChunk(std::size_t chunkIdx) const noexcept { - assert(chunkIdx < chunks.size()); - - if (chunkIdx == chunks.size() - 1) - return entityNum - (chunks.size() - 1) * chunkCapacity; - else - return chunkCapacity; -} - Ubpa::small_flat_set Archetype::GenTypeIDSet(std::span types) { small_flat_set sorted_types(types.begin(), types.end()); sorted_types.insert(TypeID_of); diff --git a/src/core/Archetype.hpp b/src/core/Archetype.hpp index a4d368e..c7c5f76 100644 --- a/src/core/Archetype.hpp +++ b/src/core/Archetype.hpp @@ -16,6 +16,11 @@ namespace Ubpa::UECS { // type of Entity + Components is Archetype's type class Archetype { public: + struct EntityAddress { + std::size_t chunkIdx; + std::size_t idxInChunk; + }; + Archetype(std::pmr::memory_resource* rsrc, std::pmr::memory_resource* world_rsrc, std::uint64_t version) noexcept; // copy @@ -41,46 +46,45 @@ namespace Ubpa::UECS { Locate(std::span cmpts) const; // nullptr if not contains - CmptAccessPtr At(AccessTypeID type, std::size_t idx) const; - CmptAccessPtr WriteAt(TypeID type, std::size_t idx) const { return At({ type, AccessMode::WRITE }, idx); } - CmptAccessPtr ReadAt(TypeID type, std::size_t idx) const { return At({ type, AccessMode::LATEST }, idx); } + CmptAccessPtr At(AccessTypeID type, EntityAddress) const; + CmptAccessPtr WriteAt(TypeID type, EntityAddress addr) const { return At({ type, AccessMode::WRITE }, addr); } + CmptAccessPtr ReadAt(TypeID type, EntityAddress addr) const { return At({ type, AccessMode::LATEST }, addr); } // nullptr if not contains template - Cmpt* WriteAt(std::size_t idx) const{ return WriteAt(TypeID_of, idx).template As(); } + Cmpt* WriteAt(EntityAddress addr) const{ return WriteAt(TypeID_of, addr).template As(); } template - const Cmpt* ReadAt(std::size_t idx) const { return ReadAt(TypeID_of, idx).template As(); } + const Cmpt* ReadAt(EntityAddress addr) const { return ReadAt(TypeID_of, addr).template As(); } // no Entity - std::vector Components(std::size_t idx, AccessMode mode) const; - std::vector WriteComponents(std::size_t idx) const { return Components(idx, AccessMode::WRITE); } - std::vector ReadComponents(std::size_t idx) const { return Components(idx, AccessMode::LATEST); } + std::vector Components(EntityAddress addr, AccessMode mode) const; + std::vector WriteComponents(EntityAddress addr) const { return Components(addr, AccessMode::WRITE); } + std::vector ReadComponents(EntityAddress addr) const { return Components(addr, AccessMode::LATEST); } // no init - std::size_t RequestBuffer(); + EntityAddress RequestBuffer(); - std::size_t Create(Entity); - - // return index in archetype - std::size_t Instantiate(Entity, std::size_t srcIdx); + EntityAddress Create(Entity); - // erase idx-th entity - // if idx != num-1, back entity will put at idx, return moved Entity's index - // else return static_cast(-1) - // move-assignment + destructor - std::size_t Erase(std::size_t idx); + // return index in archetype + EntityAddress Instantiate(Entity, EntityAddress src); + + // return moved entity index (moved to addr) + std::size_t Erase(EntityAddress addr); // Components + Entity const ArchetypeCmptTraits& GetCmptTraits() const noexcept { return cmptTraits; } - std::size_t EntityNum() const noexcept { return entityNum; } - std::size_t EntityNumOfChunk(std::size_t chunkIdx) const noexcept; - std::size_t ChunkNum() const noexcept { return chunks.size(); } - std::size_t ChunkCapacity() const noexcept { return chunkCapacity; } - // add Entity static small_flat_set GenTypeIDSet(std::span types); + std::size_t Version() const noexcept { return version; } + + std::size_t EntityNum() const noexcept { return entityNum; } + + std::span GetChunks() noexcept { return { chunks.data(), chunks.size() }; } + std::span GetOffsets() noexcept { return { offsets.data(), offsets.size() }; } + private: // set type2alignment // call after setting type2size and type2offset @@ -98,7 +102,8 @@ namespace Ubpa::UECS { small_vector chunks; std::size_t chunkCapacity{ static_cast(-1) }; small_vector offsets; // component + small_flat_set> nonFullChunks; // big -> small, use small first - std::size_t entityNum{ 0 }; // number of entities + std::size_t entityNum{ 0 }; }; } diff --git a/src/core/Chunk.cpp b/src/core/Chunk.cpp index 5512d91..eebc5ac 100644 --- a/src/core/Chunk.cpp +++ b/src/core/Chunk.cpp @@ -1,5 +1,7 @@ #include +#include "Archetype.hpp" + #include using namespace Ubpa::UECS; @@ -48,6 +50,99 @@ bool Chunk::HasAnyChange(std::span types, std::uint64_t version) c return false; } +std::size_t Chunk::Erase(std::size_t idx) { + auto* head = GetHead(); + auto& entityNum = head->num_entity; + assert(idx < entityNum); + const auto& cmptTraits = head->archetype->GetCmptTraits(); + + std::size_t dstIdxInChunk = idx; + Chunk* dstChunk = this; + std::uint8_t* dstBuffer = data; + + std::size_t movedEntityIdx; + + if (idx != entityNum - 1) { + std::size_t srcIdxInChunk = entityNum - 1; + + auto entityTypeIdx = static_cast(std::distance(cmptTraits.GetTypes().begin(), cmptTraits.GetTypes().find(TypeID_of))); + movedEntityIdx = reinterpret_cast(head->GetCmptInfos()[entityTypeIdx].offset + sizeof(Entity) * srcIdxInChunk)->index; + + for (std::size_t i = 0; i < head->num_component; i++) { + const auto& trait = cmptTraits.GetTraits()[i]; + std::size_t size = trait.size; + std::size_t offset = head->GetCmptInfos()[i].offset; + std::uint8_t* dst = data + offset + dstIdxInChunk * size; + std::uint8_t* src = data + offset + srcIdxInChunk * size; + + trait.MoveAssign(dst, src); + trait.Destruct(src); + } + } + else { + movedEntityIdx = static_cast(-1); + + if (!cmptTraits.IsTrivial()) { + for (std::size_t i = 0; i < cmptTraits.GetTypes().size(); i++) { + const auto& trait = cmptTraits.GetTraits()[i]; + std::size_t size = trait.size; + std::size_t offset = head->GetCmptInfos()[i].offset; + std::uint8_t* dst = dstBuffer + offset + dstIdxInChunk * size; + trait.Destruct(dst); + } + } + } + + head->UpdateVersion(head->archetype->Version()); + + entityNum--; + + return movedEntityIdx; +} + +void Chunk::ApplyChanges(std::span types) { + const auto infos = GetHead()->GetCmptInfos(); + for (const auto& type : types) { + if (type.GetAccessMode() != AccessMode::WRITE) + continue; + + auto target = std::lower_bound(infos.begin(), infos.end(), type, std::less<>{}); + if (target == infos.end() || target->ID != type) + continue; + + auto idx = static_cast(std::distance(infos.begin(), target)); + infos[idx].version = GetHead()->archetype->Version(); + } +} + +std::tuple, Ubpa::small_vector> Chunk::Locate(std::span types) { + const auto& cmptTraits = GetHead()->archetype->GetCmptTraits(); + const auto infos = GetHead()->GetCmptInfos(); + small_vector cmpts; + small_vector sizes; + cmpts.reserve(types.size()); + for (const auto& type : types) { + auto target = std::lower_bound(infos.begin(), infos.end(), type, std::less<>{}); + if (target == infos.end() || target->ID != type) { + cmpts.emplace_back(type, nullptr); + sizes.push_back(0); + continue; + } + + cmpts.emplace_back(type, (std::uint8_t*)data + target->offset); + auto idx = static_cast(std::distance(infos.begin(), target)); + if (type.GetAccessMode() == AccessMode::WRITE) + infos[idx].version = GetHead()->archetype->Version(); + sizes.push_back(GetHead()->archetype->GetCmptTraits().GetTraits()[idx].size); + } + + return { + (Entity*)GetCmptArray(TypeID_of), + cmpts, + sizes + }; +} + void Chunk::Head::UpdateVersion(std::uint64_t version) { order_version = version; for (auto& info : GetCmptInfos()) diff --git a/src/core/EntityMngr.cpp b/src/core/EntityMngr.cpp index 6afea16..6e698bf 100644 --- a/src/core/EntityMngr.cpp +++ b/src/core/EntityMngr.cpp @@ -41,7 +41,8 @@ EntityMngr::EntityMngr(const EntityMngr& em, std::pmr::memory_resource* world_rs for (std::size_t i = 0; i < em.entityTable.size(); i++) { auto& dst = entityTable[i]; const auto& src = em.entityTable[i]; - dst.idxInArchetype = src.idxInArchetype; + dst.chunkIdx = src.chunkIdx; + dst.idxInChunk = src.idxInChunk; dst.version = src.version; if (src.archetype) dst.archetype = ts2a.at(src.archetype->cmptTraits.GetTypes()).get(); @@ -82,7 +83,7 @@ CmptAccessPtr EntityMngr::GetComponent(Entity e, AccessTypeID type) const { assert(!type.Is()); if (!Exist(e)) throw std::invalid_argument("Entity is invalid"); const auto& info = entityTable[e.index]; - return info.archetype->At(type, info.idxInArchetype); + return info.archetype->At(type, { info.chunkIdx, info.idxInChunk }); } bool EntityMngr::Exist(Entity e) const noexcept { @@ -106,7 +107,8 @@ void EntityMngr::RecycleEntityEntry(Entity e) { auto& info = entityTable[e.index]; info.archetype = nullptr; - info.idxInArchetype = static_cast(-1); + info.chunkIdx = static_cast(-1); + info.idxInChunk = static_cast(-1); info.version++; entityTableFreeEntry.push_back(e.index); @@ -135,7 +137,9 @@ Entity EntityMngr::Create(std::span types) { auto& info = entityTable[entityIndex]; Entity e{ entityIndex, info.version }; info.archetype = archetype; - info.idxInArchetype = archetype->Create(e); + auto addr = archetype->Create(e); + info.chunkIdx = addr.chunkIdx; + info.idxInChunk = addr.idxInChunk; return e; } @@ -145,7 +149,8 @@ void EntityMngr::Attach(Entity e, std::span types) { auto& info = entityTable[e.index]; Archetype* srcArchetype = info.archetype; - std::size_t srcIdxInArchetype = info.idxInArchetype; + std::size_t srcChunkIdx = info.chunkIdx; + std::size_t srcIdxInChunk = info.idxInChunk; const auto& srcTypeIDSet = srcArchetype->GetCmptTraits().GetTypes(); auto dstTypeIDSet = srcTypeIDSet; @@ -170,28 +175,33 @@ void EntityMngr::Attach(Entity e, std::span types) { } // move src to dst - std::size_t dstIdxInArchetype = dstArchetype->RequestBuffer(); + auto dstAddr = dstArchetype->RequestBuffer(); + std::size_t dstChunkIdx = dstAddr.chunkIdx; + std::size_t dstidxInChunk = dstAddr.idxInChunk; const auto& srcCmptTraits = srcArchetype->GetCmptTraits(); for (const auto& type : srcTypeIDSet) { - auto* srcCmpt = srcArchetype->ReadAt(type, srcIdxInArchetype).Ptr(); - auto* dstCmpt = dstArchetype->WriteAt(type, dstIdxInArchetype).Ptr(); + auto* srcCmpt = srcArchetype->ReadAt(type, { srcChunkIdx, srcIdxInChunk }).Ptr(); + auto* dstCmpt = dstArchetype->WriteAt(type, { dstChunkIdx, dstidxInChunk }).Ptr(); srcCmptTraits.GetTrait(type).MoveConstruct(dstCmpt, srcCmpt); } // erase - auto srcMovedEntityIndex = srcArchetype->Erase(srcIdxInArchetype); - if (srcMovedEntityIndex != static_cast(-1)) - entityTable[srcMovedEntityIndex].idxInArchetype = srcIdxInArchetype; + auto srcMovedEntityIndex = srcArchetype->Erase({ srcChunkIdx, srcIdxInChunk }); + if (srcMovedEntityIndex != static_cast(-1)) { + entityTable[srcMovedEntityIndex].chunkIdx = srcChunkIdx; + entityTable[srcMovedEntityIndex].idxInChunk = srcIdxInChunk; + } info.archetype = dstArchetype; - info.idxInArchetype = dstIdxInArchetype; + info.chunkIdx = dstChunkIdx; + info.idxInChunk = dstidxInChunk; for (const auto& type : types) { auto target = dstArchetype->GetCmptTraits().GetTypes().find(type); assert(target != dstArchetype->GetCmptTraits().GetTypes().end()); auto idx = static_cast(std::distance(dstArchetype->GetCmptTraits().GetTypes().begin(), target)); - dstArchetype->GetCmptTraits().GetTraits()[idx].DefaultConstruct(info.archetype->WriteAt(type, info.idxInArchetype).Ptr(), world_rsrc); + dstArchetype->GetCmptTraits().GetTraits()[idx].DefaultConstruct(info.archetype->WriteAt(type, {info.chunkIdx, info.idxInChunk}).Ptr(), world_rsrc); } } @@ -228,23 +238,29 @@ void EntityMngr::Detach(Entity e, std::span types) { } // move src to dst - std::size_t srcIdxInArchetype = info.idxInArchetype; - std::size_t dstIdxInArchetype = dstArchetype->RequestBuffer(); + std::size_t srcChunkIdx = info.chunkIdx; + std::size_t srcIdxInChunk = info.idxInChunk; + auto dstAddr = dstArchetype->RequestBuffer(); + std::size_t dstChunkIdx = dstAddr.chunkIdx; + std::size_t dstidxInChunk = dstAddr.idxInChunk; const auto& srcCmptTraits = srcArchetype->GetCmptTraits(); for (const auto& type : dstTypeIDSet) { - auto* srcCmpt = srcArchetype->ReadAt(type, srcIdxInArchetype).Ptr(); - auto* dstCmpt = dstArchetype->WriteAt(type, dstIdxInArchetype).Ptr(); + auto* srcCmpt = srcArchetype->ReadAt(type, { srcChunkIdx, srcIdxInChunk }).Ptr(); + auto* dstCmpt = dstArchetype->WriteAt(type, { dstChunkIdx, dstidxInChunk }).Ptr(); srcCmptTraits.GetTrait(type).MoveConstruct(dstCmpt, srcCmpt); } // erase - std::size_t srcMovedEntityIndex = srcArchetype->Erase(srcIdxInArchetype); // call destructor - if (srcMovedEntityIndex != static_cast(-1)) - entityTable[srcMovedEntityIndex].idxInArchetype = srcIdxInArchetype; + std::size_t srcMovedEntityIndex = srcArchetype->Erase({ srcChunkIdx, srcIdxInChunk }); // call destructor + if (srcMovedEntityIndex != static_cast(-1)) { + entityTable[srcMovedEntityIndex].chunkIdx = srcChunkIdx; + entityTable[srcMovedEntityIndex].idxInChunk = srcIdxInChunk; + } info.archetype = dstArchetype; - info.idxInArchetype = dstIdxInArchetype; + info.chunkIdx = dstChunkIdx; + info.idxInChunk = dstidxInChunk; if(isNewArchetype) ts2a.emplace(std::move(dstTypeIDSet), std::unique_ptr{dstArchetype}); @@ -254,7 +270,7 @@ vector EntityMngr::Components(Entity e, AccessMode mode) const { if (!Exist(e)) throw std::invalid_argument("Entity is invalid"); const auto& info = entityTable[e.index]; - return info.archetype->Components(info.idxInArchetype, mode); + return info.archetype->Components({info.chunkIdx, info.idxInChunk}, mode); } Entity EntityMngr::Instantiate(Entity srcEntity) { @@ -264,9 +280,12 @@ Entity EntityMngr::Instantiate(Entity srcEntity) { const auto& srcInfo = entityTable[srcEntity.index]; auto& dstInfo = entityTable[dstEntityIndex]; Entity dstEntity{ dstEntityIndex, dstInfo.version }; - std::size_t dstIndexInArchetype = srcInfo.archetype->Instantiate(dstEntity, srcInfo.idxInArchetype); + auto dstAddr = srcInfo.archetype->Instantiate(dstEntity, { srcInfo.chunkIdx, srcInfo.idxInChunk }); + std::size_t dstChunkIdx = dstAddr.chunkIdx; + std::size_t dstIdxInChunk = dstAddr.idxInChunk; dstInfo.archetype = srcInfo.archetype; - dstInfo.idxInArchetype = dstIndexInArchetype; + dstInfo.chunkIdx = dstChunkIdx; + dstInfo.idxInChunk = dstIdxInChunk; return dstEntity; } @@ -307,12 +326,13 @@ void EntityMngr::Destroy(Entity e) { auto info = entityTable[e.index]; auto* archetype = info.archetype; - auto idxInArchetype = info.idxInArchetype; - auto movedEntityIndex = archetype->Erase(idxInArchetype); + auto movedEntityIndex = archetype->Erase({ info.chunkIdx, info.idxInChunk }); - if (movedEntityIndex != static_cast(-1)) - entityTable[movedEntityIndex].idxInArchetype = idxInArchetype; + if (movedEntityIndex != static_cast(-1)) { + entityTable[movedEntityIndex].chunkIdx = info.chunkIdx; + entityTable[movedEntityIndex].idxInChunk = info.idxInChunk; + } RecycleEntityEntry(e); } @@ -345,30 +365,25 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys, int layer) co assert(job); std::size_t indexOffsetInQuery = 0; for (Archetype* archetype : archetypes) { - auto [chunkEntity, chunkCmpts, sizes] = archetype->Locate(sys->entityQuery.locator.AccessTypeIDs()); - - std::size_t num = archetype->EntityNum(); - std::size_t chunkNum = archetype->ChunkNum(); - std::size_t chunkCapacity = archetype->ChunkCapacity(); - - for (std::size_t i = 0; i < chunkNum; i++) { - if (!sys->changeFilter.types.empty() && !archetype->chunks[i]->HasAnyChange(sys->changeFilter.types, version)) + std::size_t indexOffsetInArchetype = 0; + auto chunks = archetype->GetChunks(); + for (std::size_t i = 0; i < chunks.size(); i++) { + chunks[i]->ApplyChanges({ sys->entityQuery.locator.AccessTypeIDs().data(), sys->entityQuery.locator.AccessTypeIDs().size() }); + if (!sys->changeFilter.types.empty() && !chunks[i]->HasAnyChange(sys->changeFilter.types, version)) continue; - - job->emplace([=, sizes = sizes, entities = chunkEntity[i], cmpts = move(chunkCmpts[i]), singletons = singletons]() mutable { - std::size_t idxOffsetInChunk = i * chunkCapacity; - std::size_t indexOffsetInQueryChunk = indexOffsetInQuery + idxOffsetInChunk; - CmptsView chunkView{ std::span{cmpts.data(), cmpts.size()} }; + job->emplace([=, chunk = chunks[i], singletons = singletons]() { + auto [entities, cmpts, sizes] = chunk->Locate({ sys->entityQuery.locator.AccessTypeIDs().data(), sys->entityQuery.locator.AccessTypeIDs().size() }); + std::size_t indexOffsetInChunk = indexOffsetInQuery + indexOffsetInArchetype; + CmptsView cmptsView{ std::span{cmpts.data(), cmpts.size()} }; SingletonsView singletonsView{ std::span{singletons.data(), singletons.size()} }; CommandBuffer cb; - std::size_t J = min(chunkCapacity, num - idxOffsetInChunk); - for (std::size_t j = 0; j < J; j++) { + for (std::size_t j = 0; j < chunk->EntityNum(); j++) { (*sys)( w, singletonsView, entities[j], - indexOffsetInQueryChunk + j, - chunkView, + indexOffsetInChunk + j, + cmptsView, &cb ); for (std::size_t k = 0; k < cmpts.size(); k++) @@ -376,9 +391,9 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys, int layer) co } w->AddCommandBuffer(std::move(cb), layer); }); + indexOffsetInArchetype += chunks[i]->EntityNum(); } - - indexOffsetInQuery += num; + indexOffsetInQuery += indexOffsetInArchetype; } } else { @@ -386,36 +401,34 @@ bool EntityMngr::GenEntityJob(World* w, Job* job, SystemFunc* sys, int layer) co CommandBuffer cb; std::size_t indexOffsetInQuery = 0; for (Archetype* archetype : archetypes) { - auto [chunkEntity, chunkCmpts, sizes] = archetype->Locate(sys->entityQuery.locator.AccessTypeIDs()); - - std::size_t num = archetype->EntityNum(); - std::size_t chunkNum = archetype->ChunkNum(); - std::size_t chunkCapacity = archetype->ChunkCapacity(); + std::size_t indexOffsetInArchetype = 0; + auto chunks = archetype->GetChunks(); - for (std::size_t i = 0; i < chunkNum; i++) { + for (std::size_t i = 0; i < chunks.size(); i++) { + chunks[i]->ApplyChanges({ sys->entityQuery.locator.AccessTypeIDs().data(), sys->entityQuery.locator.AccessTypeIDs().size() }); if (!sys->changeFilter.types.empty() && !archetype->chunks[i]->HasAnyChange(sys->changeFilter.types, version)) continue; - std::size_t idxOffsetInChunk = i * chunkCapacity; - std::size_t indexOffsetInQueryChunk = indexOffsetInQuery + idxOffsetInChunk; - CmptsView chunkView{ std::span{chunkCmpts[i].data(), chunkCmpts[i].size()} }; + + auto [entities, cmpts, sizes] = chunks[i]->Locate({ sys->entityQuery.locator.AccessTypeIDs().data(), sys->entityQuery.locator.AccessTypeIDs().size() }); + std::size_t indexOffsetInChunk = indexOffsetInQuery + indexOffsetInArchetype; + CmptsView cmptsView{ std::span{cmpts.data(), cmpts.size()} }; SingletonsView singletonsView{ std::span{singletons.data(), singletons.size()} }; - std::size_t J = min(chunkCapacity, num - idxOffsetInChunk); - for (std::size_t j = 0; j < J; j++) { + for (std::size_t j = 0; j < chunks[i]->EntityNum(); j++) { (*sys)( w, singletonsView, - chunkEntity[i][j], - indexOffsetInQueryChunk + j, - chunkView, + entities[j], + indexOffsetInChunk + j, + cmptsView, &cb ); - for (std::size_t k = 0; k < chunkCmpts[i].size(); k++) - reinterpret_cast(chunkCmpts[i][k].p) += sizes[k]; + for (std::size_t k = 0; k < sys->entityQuery.locator.AccessTypeIDs().size(); k++) + reinterpret_cast(cmpts[k].p) += sizes[k]; } + indexOffsetInArchetype += chunks[i]->EntityNum(); } - - indexOffsetInQuery += num; + indexOffsetInQuery += indexOffsetInArchetype; } w->AddCommandBuffer(std::move(cb), layer); }; @@ -443,30 +456,33 @@ bool EntityMngr::GenChunkJob(World* w, Job* job, SystemFunc* sys, int layer) con return false; std::size_t indexOffsetInQuery = 0; for (Archetype* archetype : archetypes) { - std::size_t num = archetype->EntityNum(); - std::size_t chunkNum = archetype->ChunkNum(); - std::size_t chunkCapacity = archetype->ChunkCapacity(); - - for (std::size_t i = 0; i < chunkNum; i++) { - if (!sys->changeFilter.types.empty() && !archetype->chunks[i]->HasAnyChange(sys->changeFilter.types, version)) + std::size_t indexOffsetInArchetype = 0; + auto chunks = archetype->GetChunks(); + + for (std::size_t i = 0; i < chunks.size(); i++) { + chunks[i]->ApplyChanges({ sys->entityQuery.filter.all.data(), sys->entityQuery.filter.all.size() }); + chunks[i]->ApplyChanges({ sys->entityQuery.filter.any.data(), sys->entityQuery.filter.any.size() }); + assert(sys->entityQuery.locator.AccessTypeIDs().empty()); + if (!sys->changeFilter.types.empty() && !chunks[i]->HasAnyChange(sys->changeFilter.types, version)) continue; - std::size_t idxOffsetInChunk = i * chunkCapacity; - std::size_t indexOffsetInQueryChunk = indexOffsetInQuery + idxOffsetInChunk; + std::size_t indexOffsetInChunk = indexOffsetInQuery + indexOffsetInArchetype; + job->emplace([=, singletons = singletons]() { CommandBuffer cb; (*sys)( w, SingletonsView{ std::span{singletons.data(), singletons.size()} }, - indexOffsetInQueryChunk, + indexOffsetInChunk, archetype->chunks[i], &cb ); w->AddCommandBuffer(std::move(cb), layer); }); + indexOffsetInArchetype += chunks[i]->EntityNum(); } - indexOffsetInQuery += num; + indexOffsetInQuery += indexOffsetInArchetype; } } else { @@ -476,24 +492,27 @@ bool EntityMngr::GenChunkJob(World* w, Job* job, SystemFunc* sys, int layer) con CommandBuffer cb; std::size_t indexOffsetInQuery = 0; for (Archetype* archetype : QueryArchetypes(sys->entityQuery)) { - std::size_t num = archetype->EntityNum(); - std::size_t chunkNum = archetype->ChunkNum(); - std::size_t chunkCapacity = archetype->ChunkCapacity(); + std::size_t indexOffsetInArchetype = 0; + auto chunks = archetype->GetChunks(); - for (std::size_t i = 0; i < chunkNum; i++) { + for (std::size_t i = 0; i < chunks.size(); i++) { + chunks[i]->ApplyChanges({ sys->entityQuery.filter.all.data(), sys->entityQuery.filter.all.size() }); + chunks[i]->ApplyChanges({ sys->entityQuery.filter.any.data(), sys->entityQuery.filter.any.size() }); + assert(sys->entityQuery.locator.AccessTypeIDs().empty()); if (!sys->changeFilter.types.empty() && !archetype->chunks[i]->HasAnyChange(sys->changeFilter.types, version)) continue; - std::size_t idxOffsetInChunk = i * chunkCapacity; - std::size_t indexOffsetInQueryChunk = indexOffsetInQuery + idxOffsetInChunk; + std::size_t indexOffsetInChunk = indexOffsetInQuery + indexOffsetInArchetype; (*sys)( w, singletonsView, - indexOffsetInQueryChunk, + indexOffsetInChunk, archetype->chunks[i], &cb ); + indexOffsetInArchetype += chunks[i]->EntityNum(); } + indexOffsetInQuery += indexOffsetInArchetype; } w->AddCommandBuffer(std::move(cb), layer); }; @@ -548,14 +567,16 @@ void EntityMngr::Accept(IListener* listener) const { // TODO : speed up listener->EnterEntityMngr(this); for (const auto& [ts, a] : ts2a) { - for (std::size_t i = 0; i < a->EntityNum(); i++) { - auto e = *a->ReadAt(i); - listener->EnterEntity(e); - for (const auto& cmpt : a->Components(i, AccessMode::WRITE)) { - listener->EnterCmpt({ cmpt.AccessType(), cmpt.Ptr() }); - listener->ExistCmpt({ cmpt.AccessType(), cmpt.Ptr() }); + for (std::size_t i = 0; i < a->GetChunks().size(); i++) { + for (std::size_t j = 0; j < a->GetChunks()[i]->EntityNum(); j++) { + auto e = *a->ReadAt({ i,j }); + listener->EnterEntity(e); + for (const auto& cmpt : a->Components({ i,j }, AccessMode::WRITE)) { + listener->EnterCmpt({ cmpt.AccessType(), cmpt.Ptr() }); + listener->ExistCmpt({ cmpt.AccessType(), cmpt.Ptr() }); + } + listener->ExistEntity(e); } - listener->ExistEntity(e); } } listener->ExistEntityMngr(this); @@ -580,7 +601,7 @@ Entity EntityMngr::GetSingletonEntity(TypeID t) const { EntityQuery query{ move(filter) }; const auto& archetypes = QueryArchetypes(query); auto* archetype = *archetypes.begin(); - return *archetype->ReadAt(0); + return *archetype->ReadAt({ 0,0 }); } CmptAccessPtr EntityMngr::GetSingleton(AccessTypeID access_type) const { @@ -597,7 +618,7 @@ CmptAccessPtr EntityMngr::GetSingleton(AccessTypeID access_type) const { for (auto* archetype : archetypes) { if (archetype->EntityNum() != 0) - return archetype->At(access_type, 0); + return archetype->At(access_type, { 0,0 }); } return { TypeID{}, nullptr, AccessMode::WRITE }; diff --git a/src/core/World.cpp b/src/core/World.cpp index b9109d0..2e15d4d 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -351,13 +351,11 @@ void World::Accept(IListener* listener) const { } void World::AddCommand(std::function command, int layer) { - assert(inRunningJobGraph); std::lock_guard guard(commandBufferMutex); lcommandBuffer[layer].AddCommand(command); } void World::AddCommandBuffer(CommandBuffer cb, int layer) { - assert(inRunningJobGraph); std::lock_guard guard(commandBufferMutex); lcommandBuffer[layer].AddCommandBuffer(std::move(cb)); } diff --git a/src/test/13_performance/main.cpp b/src/test/13_performance/main.cpp index c1d2a5f..f641cf3 100644 --- a/src/test/13_performance/main.cpp +++ b/src/test/13_performance/main.cpp @@ -44,7 +44,7 @@ int main() { // about 6s // i5 10400 : 6 cores 12 threads - // about 2.6s + // about 2.7s auto d0 = t1 - t0; auto d1 = t2 - t1;