2#include "gaia/config/config.h"
7#include "gaia/cnt/darray_ext.h"
8#include "gaia/cnt/dbitset.h"
9#include "gaia/ecs/archetype.h"
10#include "gaia/ecs/command_buffer_fwd.h"
11#include "gaia/ecs/common.h"
12#include "gaia/ecs/component.h"
13#include "gaia/ecs/component_cache.h"
14#include "gaia/ecs/component_cache_item.h"
15#include "gaia/ecs/id.h"
16#include "gaia/ecs/world.h"
17#include "gaia/ser/ser_buffer_binary.h"
51 template <
typename AccessContext>
53 enum class OpType : uint8_t {
84 uint32_t m_nextTemp = 0;
86 bool m_needsSort =
false;
88 bool m_haveReal =
false;
89 bool m_haveTemp =
false;
90 Entity m_lastRealTarget = EntityBad;
91 Entity m_lastTempTarget = EntityBad;
111 GAIA_NODISCARD
Entity add(EntityKind kind = EntityKind::EK_Gen) {
114 Entity temp = add_temp(kind);
115 push_op({OpType::ADD_ENTITY, 0, temp, EntityBad});
126 push_op({OpType::CPY_ENTITY, 0, temp, entityFrom});
135 template <
typename T>
141 const auto& item = comp_cache_add<T>(m_world);
143 push_op({OpType::ADD_COMPONENT, 0, entity, item.
entity});
152 push_op({OpType::ADD_COMPONENT, 0, entity, other});
161 push_op({OpType::ADD_COMPONENT, 0, entity, (
Entity)
pair});
171 template <
typename T, std::enable_if_t<!is_pair<std::remove_cv_t<std::remove_reference_t<T>>>::value,
int> = 0>
177 const auto& item = comp_cache_add<T>(m_world);
179 const auto pos = m_data.
tell();
180 auto serializer = ser::make_serializer(m_data);
181 item.save(serializer, &value, 0, 1, 1);
182 push_op({OpType::ADD_COMPONENT_DATA, pos, entity, item.
entity});
191 template <
typename T>
197 const auto& item = comp_cache(m_world).template get<T>();
199 const auto pos = m_data.
tell();
200 auto serializer = ser::make_serializer(m_data);
201 item.save(serializer, &value, 0, 1, 1);
202 push_op({OpType::SET_COMPONENT, pos, entity, item.
entity});
210 push_op({OpType::DEL_ENTITY, 0, entity, EntityBad});
218 template <
typename T>
224 const auto& item = comp_cache(m_world).template get<T>();
226 push_op({OpType::DEL_COMPONENT, 0, entity, item.
entity});
235 push_op({OpType::DEL_COMPONENT, 0, entity,
object});
244 push_op({OpType::DEL_COMPONENT, 0, entity, (
Entity)
pair});
249 GAIA_NODISCARD
bool is_rel(OpType t)
const {
250 return (uint32_t)t >= (uint32_t)OpType::ADD_COMPONENT;
254 GAIA_NODISCARD
bool is_tmp(
Entity e)
const {
259 GAIA_NODISCARD Entity resolve(Entity e)
const {
263 const auto ti = e.id();
264 if (ti < m_temp2real.
size())
265 return m_temp2real[ti];
271 GAIA_NODISCARD Pair decode_pair(Entity pair)
const {
272 GAIA_ASSERT(pair.pair());
273 return Pair(m_world.
get(pair.id()), m_world.
get(pair.gen()));
277 void replay_add(Entity target, Entity
object) {
279 World::EntityBuilder(m_world, target).add(decode_pair(
object));
281 World::EntityBuilder(m_world, target).add(
object);
285 void replay_del(Entity target, Entity
object) {
287 World::EntityBuilder(m_world, target).del(decode_pair(
object));
289 World::EntityBuilder(m_world, target).del(
object);
294 GAIA_NODISCARD
bool is_canceled_temp(uint32_t idx)
const {
295 const uint32_t base = idx * 3;
296 if (base + 2 >= m_tmpFlags.
size())
299 const bool destroy = m_tmpFlags.test(base);
300 const bool usedOther = m_tmpFlags.test(base + 2);
303 return destroy && !usedOther;
307 GAIA_NODISCARD Entity add_temp(EntityKind kind) {
309 Entity tmp(m_nextTemp++, 0,
true,
false, kind);
315 GAIA_NODISCARD
bool less_target(Entity a, Entity b)
const {
316 const bool ta = is_tmp(a);
317 const bool tb = is_tmp(b);
324 return a.id() < b.id();
328 void check_sort(
const Op& op) {
329 if (is_tmp(op.target)) {
332 const uint32_t prev = m_lastTempTarget.
id();
333 const uint32_t curr = op.target.id();
337 m_lastTempTarget = op.target;
342 if (op.target.id() < m_lastRealTarget.
id())
345 m_lastRealTarget = op.target;
351 void push_op(Op&& op) {
367 m_lastRealTarget = EntityBad;
368 m_lastTempTarget = EntityBad;
379 GAIA_PROF_SCOPE(cmdbuf::commit);
382 if (m_nextTemp > 0) {
383 GAIA_PROF_SCOPE(cmdbuf::alloc);
389 m_tmpFlags.
resize(m_nextTemp * 3);
392 if (m_temp2real.
size() < m_nextTemp) {
393 const auto from = m_temp2real.
size();
394 m_temp2real.
resize(m_nextTemp);
395 GAIA_FOR2(from, m_nextTemp) m_temp2real[i] = EntityBad;
399 for (
const Op& o: m_ops) {
401 if (is_tmp(o.target)) {
402 const uint32_t ti = o.target.id();
404 if (o.type == OpType::DEL_ENTITY)
405 m_tmpFlags.set((ti * 3) + 0,
true);
406 else if (is_rel(o.type))
407 m_tmpFlags.set((ti * 3) + 1,
true);
410 if (is_tmp(o.other) && o.other.id() < m_tmpFlags.
size())
411 m_tmpFlags.set((o.other.id() * 3) + 2,
true);
415 for (
const Op& o: m_ops) {
416 if (!is_tmp(o.target))
419 const uint32_t ti = o.target.id();
420 if (is_canceled_temp(ti))
423 if (o.type == OpType::ADD_ENTITY) {
424 if (m_temp2real[ti] == EntityBad)
425 m_temp2real[ti] = m_world.
add(o.target.kind());
426 }
else if (o.type == OpType::CPY_ENTITY) {
427 if (m_temp2real[ti] == EntityBad) {
428 const Entity src = resolve(o.other);
429 if (src != EntityBad)
430 m_temp2real[ti] = m_world.
copy(src);
439 GAIA_PROF_SCOPE(cmdbuf::sort);
442 core::sort(m_ops.
begin(), m_ops.
end(), [](
const Op& a,
const Op& b) {
443 if (a.target != b.target)
444 return a.target < b.target;
445 if (a.other != b.other)
446 return a.other < b.other;
452 Entity lastKey = EntityBad;
453 Entity lastResolved = EntityBad;
454 auto resolve_cached = [&](
Entity e) {
458 return lastResolved = resolve(e);
462 GAIA_PROF_SCOPE(cmdbuf::merges);
463 for (uint32_t p = 0; p < m_ops.
size();) {
464 GAIA_PROF_SCOPE(cmdbuf::merge);
466 const Entity tgtKey = m_ops[p].target;
468 const bool tgtIsTemp = is_tmp(tgtKey);
469 const uint32_t ti = tgtIsTemp ? tgtKey.
id() : 0u;
471 tgtIsTemp ? (ti < m_temp2real.
size() ? m_temp2real[ti] : EntityBad) : resolve_cached(tgtKey);
475 bool hasDelEntity =
false;
476 while (q < m_ops.
size() && m_ops[q].target == tgtKey) {
477 if (m_ops[q].type == OpType::DEL_ENTITY)
483 if (tgtReal == EntityBad) {
487 if (tgtIsTemp && is_canceled_temp(ti)) {
492 enum : uint8_t { F_ADD = 1 << 0, F_ADD_DATA = 1 << 1, F_SET = 1 << 2, F_DEL = 1 << 3 };
497 for (uint32_t i = p; i < q;) {
498 const Entity othKey = m_ops[i].other;
499 const Entity othReal = resolve_cached(othKey);
503 while (j < q && m_ops[j].other == othKey)
506 if (tgtReal != EntityBad) {
507 const uint32_t groupSize = j - i;
509 if (groupSize == 1) {
510 const Op& op = m_ops[i];
512 case OpType::DEL_COMPONENT:
513 replay_del(tgtReal, othReal);
515 case OpType::ADD_COMPONENT:
516 replay_add(tgtReal, othReal);
518 case OpType::ADD_COMPONENT_DATA:
519 replay_add(tgtReal, othReal);
521 case OpType::SET_COMPONENT: {
522 const auto& ec = m_world.m_recs.entities[tgtReal.
id()];
523 const auto row = tgtReal.
kind() == EntityKind::EK_Uni ? 0U : ec.row;
524 const auto compIdx = ec.pChunk->comp_idx(othReal);
525 auto* pComponentData = (
void*)ec.pChunk->comp_ptr_mut(compIdx, 0);
528 auto serializer = ser::make_serializer(m_data);
529 serializer.seek(op.off);
530 const auto& item = m_world.
comp_cache().get(othReal);
531 item.load(serializer, pComponentData, row, row + 1, ec.pChunk->capacity());
540 uint32_t dataPos = 0;
542 for (uint32_t k = i; k < j; ++k) {
543 const Op& op = m_ops[k];
545 case OpType::ADD_COMPONENT:
548 case OpType::ADD_COMPONENT_DATA:
552 case OpType::SET_COMPONENT:
556 case OpType::DEL_COMPONENT:
564 const bool hasAdd = mask & F_ADD;
565 const bool hasAddData = mask & F_ADD_DATA;
566 const bool hasSet = mask & F_SET;
567 const bool hasDel = mask & F_DEL;
570 if (hasDel && (hasAdd || hasAddData)) {
574 replay_del(tgtReal, othReal);
577 else if (hasAddData || (hasAdd && hasSet)) {
578 replay_add(tgtReal, othReal);
580 const auto& ec = m_world.m_recs.entities[tgtReal.
id()];
581 const auto row = tgtReal.
kind() == EntityKind::EK_Uni ? 0U : ec.row;
582 const auto compIdx = ec.pChunk->comp_idx(othReal);
583 auto* pComponentData = (
void*)ec.pChunk->comp_ptr_mut(compIdx, 0);
586 auto serializer = ser::make_serializer(m_data);
587 serializer.seek(dataPos);
588 const auto& item = m_world.
comp_cache().get(othReal);
589 item.load(serializer, pComponentData, row, row + 1, ec.pChunk->capacity());
593 replay_add(tgtReal, othReal);
597 const auto& ec = m_world.m_recs.entities[tgtReal.
id()];
598 const auto row = tgtReal.
kind() == EntityKind::EK_Uni ? 0U : ec.row;
599 const auto compIdx = ec.pChunk->comp_idx(othReal);
600 auto* pComponentData = (
void*)ec.pChunk->comp_ptr_mut(compIdx, 0);
603 auto serializer = ser::make_serializer(m_data);
604 serializer.seek(dataPos);
605 const auto& item = m_world.
comp_cache().get(othReal);
606 item.load(serializer, pComponentData, row, row + 1, ec.pChunk->capacity());
617 m_world.
del(tgtReal);
629 using CommandBufferST = detail::CommandBuffer<AccessContextST>;
630 using CommandBufferMT = detail::CommandBuffer<AccessContextMT>;
632 inline CommandBufferST* cmd_buffer_st_create(World& world) {
633 return new CommandBufferST(world);
635 inline void cmd_buffer_destroy(CommandBufferST& cmdBuffer) {
638 inline void cmd_buffer_commit(CommandBufferST& cmdBuffer) {
642 inline CommandBufferMT* cmd_buffer_mt_create(World& world) {
643 return new CommandBufferMT(world);
645 inline void cmd_buffer_destroy(CommandBufferMT& cmdBuffer) {
648 inline void cmd_buffer_commit(CommandBufferMT& cmdBuffer) {
Array with variable size of elements of type.
Definition darray_impl.h:27
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_impl.h:504
void clear() noexcept
Removes all elements.
Definition darray_impl.h:449
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_impl.h:556
void resize(size_type count)
Changes the number of elements.
Definition darray_impl.h:240
GAIA_NODISCARD bool empty() const noexcept
Checks whether the container has no elements.
Definition darray_impl.h:510
void push_back(const T &arg)
Appends an element.
Definition darray_impl.h:309
GAIA_NODISCARD auto end() noexcept
Returns an iterator one past the last element.
Definition darray_impl.h:592
Owns entities, components, archetypes, queries, observers, and systems.
Definition world.h:80
GAIA_NODISCARD const ComponentCache & comp_cache() const
Returns read-only access to the world component cache.
Definition world.h:3321
void del(Entity entity)
Removes an entity along with all data associated with it.
Definition world.h:5791
GAIA_NODISCARD Entity get(EntityId id) const
Returns the entity located at the index id.
Definition world.h:3767
GAIA_NODISCARD Entity copy(Entity srcEntity)
Creates a new entity by cloning an already existing one. Does not trigger observers.
Definition world.h:4146
GAIA_NODISCARD Entity add(EntityKind kind=EntityKind::EK_Gen)
Creates a new empty entity.
Definition world.h:3817
Buffer for deferred execution of some operations on entities.
Definition command_buffer.h:52
void add(Entity entity, Entity other)
Requests an entity other to be added to entity entity.
Definition command_buffer.h:149
void add(Entity entity)
Requests a component T to be added to entity.
Definition command_buffer.h:136
void del(Entity entity)
Requests an existing entity to be removed.
Definition command_buffer.h:207
void add(Entity entity, const Pair &pair)
Requests a relationship pair to be added to entity entity.
Definition command_buffer.h:158
void commit()
Commits all queued changes.
Definition command_buffer.h:373
GAIA_NODISCARD Entity add(EntityKind kind=EntityKind::EK_Gen)
Requests a new entity to be created.
Definition command_buffer.h:111
void add(Entity entity, T &&value)
Requests a component T to be added to entity. Also sets its value.
Definition command_buffer.h:172
GAIA_NODISCARD Entity copy(Entity entityFrom)
Requests a new entity to be created by cloning an already existing entity.
Definition command_buffer.h:122
void del(Entity entity)
Requests removal of component T from entity.
Definition command_buffer.h:219
void set(Entity entity, T &&value)
Requests component data to be set to given values for a given entity.
Definition command_buffer.h:192
void del(Entity entity, Entity object)
Requests removal of entity object from entity entity.
Definition command_buffer.h:232
void del(Entity entity, const Pair &pair)
Requests removal of a relationship pair from entity entity.
Definition command_buffer.h:241
Wrapper for two Entities forming a relationship pair.
Definition id.h:614
Wrapper for two types forming a relationship pair. Depending on what types are used to form a pair it...
Definition id.h:262
Non-recursive spin lock backed by an atomic flag.
Definition spinlock.h:9
void lock()
Spins until the lock is acquired.
Definition spinlock.h:26
void unlock()
Releases the lock.
Definition spinlock.h:39
Default in-memory binary backend used by ECS world/runtime serialization. Provides aligned raw read/w...
Definition ser_binary.h:12
uint32_t tell() const
Returns current stream cursor position in bytes.
Definition ser_binary.h:48
void reset()
Clears buffered data and resets stream position.
Definition ser_binary.h:43
RAII helper that calls lock() on construction and unlock() on destruction.
Definition utility.h:188
Multi-threaded command-buffer access guard backed by a spin lock.
Definition command_buffer.h:30
mt::SpinLock m_lock
Spin lock serializing command-buffer access across worker threads.
Definition command_buffer.h:32
void lock()
Acquires the access guard.
Definition command_buffer.h:35
void unlock()
Releases the access guard.
Definition command_buffer.h:40
Single-threaded command-buffer access guard. Locking is a no-op.
Definition command_buffer.h:22
void unlock()
Releases the access guard.
Definition command_buffer.h:26
void lock()
Acquires the access guard.
Definition command_buffer.h:24
IdentifierData tmp
0-real entity, 1-temporary entity
Definition id.h:320
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:296
InternalData data
Structured view of the packed value.
Definition id.h:328
GAIA_NODISCARD constexpr auto kind() const noexcept
Entity kind of this id.
Definition id.h:389
GAIA_NODISCARD constexpr auto id() const noexcept
Entity index in the entity array.
Definition id.h:359
GAIA_NODISCARD constexpr bool entity() const noexcept
Whether this id refers to an entity.
Definition id.h:371