Skip to content

Commit 76fec88

Browse files
committed
rework linear allocators option in entities manager
1 parent 03ea476 commit 76fec88

File tree

4 files changed

+50
-42
lines changed

4 files changed

+50
-42
lines changed

sources/include/cage-core/entities.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ namespace cage
5454
EntityComponent *defineComponent_(uint32 typeIndex, const void *prototype);
5555
};
5656

57-
CAGE_CORE_API Holder<EntityManager> newEntityManager();
57+
struct CAGE_CORE_API EntityManagerCreateConfig
58+
{
59+
bool linearAllocators = false; // much faster purge, but destroying entities or components will keep the memory allocated until purged
60+
};
61+
62+
CAGE_CORE_API Holder<EntityManager> newEntityManager(const EntityManagerCreateConfig &config = {});
5863

5964
class CAGE_CORE_API EntityComponent : private Immovable
6065
{
@@ -141,7 +146,7 @@ namespace cage
141146
const EntityManager *source = nullptr;
142147
EntityManager *destination = nullptr;
143148
bool purge = true; // faster destroy previous entities, but does not call any callbacks
144-
bool linearAllocator = false; // replace allocators in destination, which allows faster copy, but forbids removing entities or components
149+
bool rebuildIndices = false; // allows correctly removing entities/components
145150
};
146151

147152
CAGE_CORE_API void entitiesCopy(const EntitiesCopyConfig &config);

sources/libcore/entities.cpp

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,20 @@ namespace cage
3131
class EntityManagerImpl : public EntityManager
3232
{
3333
public:
34+
const EntityManagerCreateConfig config;
3435
Holder<MemoryArena> arena;
3536
std::vector<Holder<ComponentImpl>> components;
3637
std::vector<EntityComponent *> componentsByTypes;
3738
ankerl::unordered_dense::map<uint32, Entity *> namedEntities;
3839
FlatBag<Entity *> allEntities;
3940
uint32 generateId = 0;
4041
uint32 entSize = 0;
41-
bool linearArena = false;
42+
43+
EntityManagerImpl(const EntityManagerCreateConfig &config) : config(config) {}
4244

4345
~EntityManagerImpl()
4446
{
45-
destroy();
47+
purge();
4648
components.clear();
4749
}
4850

@@ -66,12 +68,19 @@ namespace cage
6668
}
6769
catch (...)
6870
{
69-
arena->deallocate(ptr);
71+
if (!config.linearAllocators)
72+
arena->deallocate(ptr);
7073
throw;
7174
}
7275
}
7376

74-
void desEnt(EntityImpl *e) { arena->destroy<EntityImpl>(e); }
77+
void desEnt(EntityImpl *e)
78+
{
79+
if (config.linearAllocators)
80+
e->~EntityImpl();
81+
else
82+
arena->destroy<EntityImpl>(e);
83+
}
7584
};
7685

7786
class ComponentImpl : public EntityComponent
@@ -91,12 +100,19 @@ namespace cage
91100
CAGE_ASSERT(typeSize > 0);
92101
prototype = systemMemory().createBuffer(typeSize, typeAlignment).cast<void>();
93102
detail::memcpy(+prototype, prototype_, typeSize);
94-
arena = newMemoryAllocatorPool({ std::max(typeSize, uint32(sizeof(uintPtr))), typeAlignment });
103+
if (manager->config.linearAllocators)
104+
arena = newMemoryAllocatorLinear({});
105+
else
106+
arena = newMemoryAllocatorPool({ std::max(typeSize, uint32(sizeof(uintPtr))), typeAlignment });
95107
}
96108

97109
void *newVal() { return arena->allocate(typeSize, typeAlignment); }
98110

99-
void desVal(void *v) { arena->deallocate(v); }
111+
void desVal(void *v)
112+
{
113+
if (!manager->config.linearAllocators)
114+
arena->deallocate(v);
115+
}
100116
};
101117

102118
EntityImpl::EntityImpl(EntityManagerImpl *manager, uint32 id) : manager(manager), id(id)
@@ -244,13 +260,11 @@ namespace cage
244260
void EntityManager::destroy()
245261
{
246262
EntityManagerImpl *impl = (EntityManagerImpl *)this;
247-
if (impl->linearArena)
248-
impl->purge();
249-
else
250-
{
251-
while (!impl->allEntities.empty())
252-
impl->allEntities.unsafeData().back()->destroy();
253-
}
263+
// first call all callbacks
264+
while (!impl->allEntities.empty())
265+
impl->allEntities.unsafeData().back()->destroy();
266+
// second free the memory
267+
impl->purge();
254268
}
255269

256270
void EntityManager::purge()
@@ -285,7 +299,10 @@ namespace cage
285299
ComponentImpl *c = +h;
286300
impl->components.push_back(std::move(h));
287301
impl->entSize = sizeof(EntityImpl) + impl->components.size() * sizeof(void *);
288-
impl->arena = newMemoryAllocatorPool({ impl->entSize, alignof(EntityImpl) });
302+
if (impl->config.linearAllocators)
303+
impl->arena = newMemoryAllocatorLinear({});
304+
else
305+
impl->arena = newMemoryAllocatorPool({ impl->entSize, alignof(EntityImpl) });
289306
return c;
290307
}
291308

@@ -294,9 +311,9 @@ namespace cage
294311
return defineComponent_(source->typeIndex(), +((ComponentImpl *)source)->prototype);
295312
}
296313

297-
Holder<EntityManager> newEntityManager()
314+
Holder<EntityManager> newEntityManager(const EntityManagerCreateConfig &config)
298315
{
299-
return systemMemory().createImpl<EntityManager, EntityManagerImpl>();
316+
return systemMemory().createImpl<EntityManager, EntityManagerImpl>(config);
300317
}
301318

302319
EntityManager *EntityComponent::manager() const
@@ -437,7 +454,6 @@ namespace cage
437454
const EntityManagerImpl *src = (const EntityManagerImpl *)config.source;
438455
EntityManagerImpl *dst = (EntityManagerImpl *)config.destination;
439456

440-
CAGE_ASSERT(!config.linearAllocator || config.purge); // linear arena must purge
441457
if (config.purge)
442458
dst->purge();
443459
else
@@ -446,19 +462,6 @@ namespace cage
446462
// must define components before changing the allocators
447463
const auto mp = componnentMapping(config);
448464

449-
CAGE_ASSERT(!dst->linearArena || config.linearAllocator); // cannot take back
450-
if (config.linearAllocator && !dst->linearArena)
451-
{
452-
dst->linearArena = true;
453-
for (const auto &it : dst->components)
454-
{
455-
it->arena.clear();
456-
it->arena = newMemoryAllocatorLinear({});
457-
}
458-
dst->arena.clear();
459-
dst->arena = newMemoryAllocatorLinear({});
460-
}
461-
462465
ankerl::unordered_dense::map<Entity *, EntityImpl *> ents; // old -> new
463466
ents.reserve(src->count());
464467
dst->allEntities.unsafeData().reserve(src->allEntities.size());
@@ -471,7 +474,7 @@ namespace cage
471474
dst->allEntities.unsafeData().push_back(n);
472475
ents[se] = n;
473476
}
474-
if (!config.linearAllocator)
477+
if (config.rebuildIndices)
475478
dst->allEntities.unsafeRebuildIndex();
476479
for (Entity *e : dst->allEntities)
477480
{
@@ -491,7 +494,7 @@ namespace cage
491494
detail::memcpy(de->comp(di) = dc->newVal(), se->comp(si), sz);
492495
dc->componentEntities.unsafeData().push_back(de);
493496
}
494-
if (!config.linearAllocator)
497+
if (config.rebuildIndices)
495498
dc->componentEntities.unsafeRebuildIndex();
496499
}
497500
}

sources/libcore/memory/allocators.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,15 @@ namespace cage
151151

152152
void addBlock(MemoryBuffer &b)
153153
{
154-
char *begin = b.data();
155-
char *end = b.data() + b.size();
156-
char *p = (char *)detail::roundDownTo((uintPtr)end, config.itemAlignment) - config.itemSize;
157-
while (p >= begin)
154+
char *p = (char *)detail::roundUpTo((uintPtr)b.data(), config.itemAlignment);
155+
freeList = p;
156+
char *e = b.data() + b.size() - config.itemSize * 2;
157+
while (p < e)
158158
{
159-
deallocate(p);
160-
p -= config.itemSize; // form the free list in reverse so that consecutive allocations have increasing addresses
159+
*(void **)p = p + config.itemSize;
160+
p += config.itemSize;
161161
}
162+
*(void **)p = nullptr;
162163
}
163164

164165
void newBlock()

sources/libsimple/graphics.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace cage
6262

6363
struct EmitBuffer : private Immovable
6464
{
65-
Holder<EntityManager> scene = newEntityManager();
65+
Holder<EntityManager> scene = newEntityManager({ .linearAllocators = true });
6666
uint64 emitTime = 0;
6767
};
6868

@@ -192,7 +192,6 @@ namespace cage
192192
EntitiesCopyConfig cfg;
193193
cfg.source = engineEntities();
194194
cfg.destination = +emitBuffers[lock.index()].scene;
195-
cfg.linearAllocator = true;
196195
entitiesCopy(cfg);
197196
emitBuffers[lock.index()].emitTime = emitTime;
198197
}

0 commit comments

Comments
 (0)