Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
chunk.h
1#pragma once
2#include "gaia/config/config.h"
3#include "gaia/config/profiler.h"
4
5#include <cstdint>
6#include <cstring>
7#include <tuple>
8#include <type_traits>
9#include <utility>
10
11#include "gaia/cnt/sarray_ext.h"
12#include "gaia/core/utility.h"
13#include "gaia/ecs/archetype_common.h"
14#include "gaia/ecs/chunk_allocator.h"
15#include "gaia/ecs/chunk_header.h"
16#include "gaia/ecs/common.h"
17#include "gaia/ecs/component.h"
18#include "gaia/ecs/component_cache.h"
19#include "gaia/ecs/component_desc.h"
20#include "gaia/ecs/entity_container.h"
21#include "gaia/ecs/id.h"
22#include "gaia/mem/data_layout_policy.h"
23#include "gaia/mem/mem_alloc.h"
24#include "gaia/ser/ser_binary.h"
25#include "gaia/ser/ser_rt.h"
26
27namespace gaia {
28 namespace ecs {
29 class World;
30 class Chunk;
31 void world_invalidate_sorted_queries_for_entity(World& world, Entity entity);
32 void world_invalidate_sorted_queries(World& world);
33 void world_notify_on_set(World& world, Entity term, Chunk& chunk, uint16_t from, uint16_t to);
34
36 class GAIA_API Chunk final {
37 public:
44
45 private:
47 ChunkHeader m_header;
49 ChunkRecords m_records;
50
62 uint8_t m_data[1];
63
64 GAIA_MSVC_WARNING_PUSH()
65 GAIA_MSVC_WARNING_DISABLE(26495)
66
67 Chunk(
68 const World& wld, const ComponentCache& cc, //
69 uint32_t chunkIndex, uint16_t capacity, //
70 uint32_t& worldVersion): //
71 m_header(wld, cc, chunkIndex, capacity, worldVersion) {
72 // Chunk data area consist of memory offsets, entities, and component data. Normally, we would need
73 // to in-place construct all of it manually.
74 // However, the memory offsets and entities are all trivial types and components are initialized via
75 // their constructors on-demand (if not trivial) so we do not really need to do any construction here.
76 }
77
78 GAIA_MSVC_WARNING_POP()
79
80 GAIA_CLANG_WARNING_PUSH()
81 // Memory is aligned so we can silence this warning
82 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
83
86 void init(
87 uint8_t* pDataArea, uint32_t cntEntities, const Entity* ids, const ComponentCacheItem* const* pItems,
88 const ChunkDataOffsets& headerOffsets, const ChunkDataOffset* compOffs) {
89 m_header.cntEntities = (uint8_t)cntEntities;
90
91 // Cache pointers to versions
92 m_records.pVersions = (ComponentVersion*)(pDataArea + headerOffsets.firstByte_Versions);
93
94 // Cache entity ids
95 if (cntEntities > 0) {
96 auto* dst = m_records.pCompEntities = (Entity*)(pDataArea + headerOffsets.firstByte_CompEntities);
97
98 // We treat the entity array as if were MAX_COMPONENTS long.
99 // Real size can be smaller.
100 uint32_t j = 0;
101 for (; j < cntEntities; ++j)
102 dst[j] = ids[j];
103 for (; j < ChunkHeader::MAX_COMPONENTS; ++j)
104 dst[j] = EntityBad;
105 }
106
107 // Cache component records
108 if (cntEntities > 0) {
109 auto* dst = m_records.pRecords = (ComponentRecord*)(pDataArea + headerOffsets.firstByte_Records);
110 GAIA_FOR_(cntEntities, j) {
111 dst[j].comp = pItems[j] == nullptr //
112 ? Component(IdentifierIdBad, 0, 0, 0, DataStorageType::Table)
113 : archetype_component(ids[j], pItems[j]->comp);
114 dst[j].pData = pDataArea + compOffs[j];
115 dst[j].pItem = pItems[j];
116 }
117 }
118
119 m_records.pEntities = (Entity*)(pDataArea + headerOffsets.firstByte_EntityData);
120
121 // Now that records are set, we use the cached component descriptors to set ctor/dtor masks.
122 {
123 auto recs = comp_rec_view();
124 const auto recs_cnt = recs.size();
125 GAIA_FOR(recs_cnt) {
126 const auto& rec = recs[i];
127 if (!component_uses_table_storage(rec.comp))
128 continue;
129
130 m_header.hasAnyCustomCtor |= (rec.pItem->func_ctor != nullptr);
131 m_header.hasAnyCustomDtor |= (rec.pItem->func_dtor != nullptr);
132 }
133 }
134
135 // Make sure world versions are set initially.
136 update_world_version_init();
137 }
138
139 GAIA_CLANG_WARNING_POP()
140
141
143 GAIA_NODISCARD std::span<const ComponentVersion> comp_version_view() const {
144 return {(const ComponentVersion*)m_records.pVersions, (size_t)m_header.cntEntities + 1};
145 }
146
149 GAIA_NODISCARD std::span<ComponentVersion> comp_version_view_mut() {
150 return {m_records.pVersions, (size_t)m_header.cntEntities + 1};
151 }
152
153 GAIA_NODISCARD std::span<Entity> entity_view_mut() {
154 return {m_records.pEntities, m_header.count};
155 }
156
164 template <typename T>
165 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) const //
166 -> decltype(std::span<const uint8_t>{}) {
167
168 if constexpr (std::is_same_v<core::raw_t<T>, Entity>) {
169 GAIA_ASSERT(to <= m_header.count);
170 return {(const uint8_t*)&m_records.pEntities[from], to - from};
171 } else if constexpr (is_pair<T>::value) {
172 using TT = typename T::type;
173 using U = typename component_type_t<TT>::Type;
174 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
175
176 if constexpr (mem::is_soa_layout_v<U>) {
177 GAIA_ASSERT(from == 0);
178 GAIA_ASSERT(to == capacity());
179 return {comp_ptr(compIdx), to};
180 } else {
181 GAIA_ASSERT(to <= m_header.count);
182 return {comp_ptr(compIdx, from), to - from};
183 }
184 } else {
185 using U = typename component_type_t<T>::Type;
186 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
187
188 if constexpr (mem::is_soa_layout_v<U>) {
189 GAIA_ASSERT(from == 0);
190 GAIA_ASSERT(to == capacity());
191 return {comp_ptr(compIdx), to};
192 } else {
193 GAIA_ASSERT(to <= m_header.count);
194 return {comp_ptr(compIdx, from), to - from};
195 }
196 }
197 }
198
205 template <typename T>
206 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter(uint32_t from, uint32_t to) const //
207 -> decltype(std::span<const uint8_t>{}) {
208 if constexpr (std::is_same_v<core::raw_t<T>, Entity>)
209 return view_inter_idx<T>(BadIndex, from, to);
210 else if constexpr (is_pair<T>::value) {
211 const auto rel = m_header.cc->get<typename T::rel>().entity;
212 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
213 return view_inter_idx<T>(comp_idx((Entity)Pair(rel, tgt)), from, to);
214 } else {
215 const auto comp = m_header.cc->get<T>().entity;
216 return view_inter_idx<T>(comp_idx(comp), from, to);
217 }
218 }
219
228 template <typename T, bool WorldVersionUpdateWanted>
229 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) //
230 -> decltype(std::span<uint8_t>{}) {
231 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "view_mut can't be used to modify Entity");
232
233 if constexpr (is_pair<T>::value) {
234 using TT = typename T::type;
235 using U = typename component_type_t<TT>::Type;
236 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
237
238 // Update version number if necessary so we know RW access was used on the chunk
239 if constexpr (WorldVersionUpdateWanted) {
240 update_world_version(compIdx);
241
242#if GAIA_ENABLE_SET_HOOKS
243 const auto& rec = m_records.pRecords[compIdx];
244 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
245 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
246#endif
247 }
248
249 if constexpr (mem::is_soa_layout_v<U>) {
250 GAIA_ASSERT(from == 0);
251 GAIA_ASSERT(to == capacity());
252 return {comp_ptr_mut(compIdx), to};
253 } else {
254 GAIA_ASSERT(to <= m_header.count);
255 return {comp_ptr_mut(compIdx, from), to - from};
256 }
257 } else {
258 using U = typename component_type_t<T>::Type;
259 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
260
261 // Update version number if necessary so we know RW access was used on the chunk
262 if constexpr (WorldVersionUpdateWanted) {
263 update_world_version(compIdx);
264
265#if GAIA_ENABLE_SET_HOOKS
266 const auto& rec = m_records.pRecords[compIdx];
267 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
268 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
269#endif
270 }
271
272 if constexpr (mem::is_soa_layout_v<U>) {
273 GAIA_ASSERT(from == 0);
274 GAIA_ASSERT(to == capacity());
275 return {comp_ptr_mut(compIdx), to};
276 } else {
277 GAIA_ASSERT(to <= m_header.count);
278 return {comp_ptr_mut(compIdx, from), to - from};
279 }
280 }
281 }
282
283 template <typename T, bool WorldVersionUpdateWanted>
284 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter(uint32_t from, uint32_t to) //
285 -> decltype(std::span<uint8_t>{}) {
286 if constexpr (is_pair<T>::value) {
287 const auto rel = m_header.cc->get<typename T::rel>().entity;
288 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
289 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx((Entity)Pair(rel, tgt)), from, to);
290 } else {
291 const auto comp = m_header.cc->get<T>().entity;
292 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx(comp), from, to);
293 }
294 }
295
296 public:
303 template <bool WorldVersionUpdateWanted>
304 GAIA_NODISCARD GAIA_FORCEINLINE auto comp_ptr_mut_gen(uint32_t compIdx, uint32_t row) {
305 // Update version number if necessary so we know RW access was used on the chunk
306 if constexpr (WorldVersionUpdateWanted) {
307 update_world_version(compIdx);
308
309#if GAIA_ENABLE_SET_HOOKS
310 const auto& rec = m_records.pRecords[compIdx];
311 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
312 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
313#endif
314 }
315
316 return comp_ptr_mut(compIdx, row);
317 }
318
321 void finish_write(uint32_t compIdx, uint16_t from, uint16_t to) {
322 GAIA_ASSERT(compIdx < m_header.cntEntities);
323 if (from >= to)
324 return;
325
326 update_world_version(compIdx);
327
328#if GAIA_ENABLE_SET_HOOKS
329 const auto& rec = m_records.pRecords[compIdx];
330 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
331 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
332#endif
333
334 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, from, to);
335 }
336
337 private:
344 template <typename T>
345 GAIA_NODISCARD decltype(auto) comp_inter(uint16_t row) const {
346 using U = typename actual_type_t<T>::Type;
347 using RetValueType = decltype(view<T>()[0]);
348
349 GAIA_ASSERT(row < m_header.count);
350 if constexpr (mem::is_soa_layout_v<U>)
351 return view<T>(0, capacity())[row];
352 else if constexpr (sizeof(RetValueType) <= 8)
353 return view<T>()[row];
354 else
355 return (const U&)view<T>()[row];
356 }
357
358 template <typename T>
359 GAIA_NODISCARD decltype(auto) comp_inter_idx(uint16_t row, uint32_t compIdx) const {
360 using U = typename actual_type_t<T>::Type;
361 using RetValueType = decltype(view_raw<T>((const void*)nullptr, 1)[0]);
362
363 GAIA_ASSERT(row < m_header.count);
364 if constexpr (mem::is_soa_layout_v<U>)
365 return view_raw<T>(comp_ptr(compIdx), capacity())[row];
366 else if constexpr (sizeof(RetValueType) <= 8)
367 return view_raw<T>(comp_ptr(compIdx, row), 1)[0];
368 else
369 return (const U&)view_raw<T>(comp_ptr(compIdx, row), 1)[0];
370 }
371
372 template <typename T, bool WorldVersionUpdateWanted>
373 GAIA_NODISCARD decltype(auto) comp_mut_idx(uint16_t row, uint32_t compIdx) {
374 using U = typename actual_type_t<T>::Type;
375
376 GAIA_ASSERT(row < m_header.capacity);
377 if constexpr (mem::is_soa_layout_v<U>)
378 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), capacity())[row];
379 else
380 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, row), 1)[0];
381 }
382
383 public:
384 Chunk(const Chunk& chunk) = delete;
385 Chunk(Chunk&& chunk) = delete;
386 Chunk& operator=(const Chunk& chunk) = delete;
387 Chunk& operator=(Chunk&& chunk) = delete;
388 ~Chunk() = default;
389
394 static constexpr uint16_t chunk_header_size() {
395 // Chunk "header" area (before actual entity/component data starts)
396 constexpr uint32_t chunkBytes = sizeof(ChunkHeader) + sizeof(ChunkRecords);
397#if GAIA_ECS_CHUNK_ALLOCATOR
398 // Blocks are MemoryBlockAlignment-aligned and the chunk follows the block header so the padding is known
399 static_assert(MemoryBlockUsableOffset % alignof(Chunk) == 0);
400 constexpr uint32_t headerBytes = mem::align<MemoryBlockAlignment>(MemoryBlockUsableOffset + chunkBytes);
401#else
402 // Heap memory has no MemoryBlockAlignment guarantee so reserve the largest possible padding
403 constexpr uint32_t headerBytes = chunkBytes + MemoryBlockAlignment - 1;
404#endif
405 static_assert(headerBytes < UINT16_MAX);
406 return (uint16_t)headerBytes;
407 }
408
412 static constexpr uint16_t chunk_total_bytes(uint16_t dataSize) {
413 return chunk_header_size() + dataSize;
414 }
415
419 static constexpr uint16_t chunk_data_bytes(uint16_t totalSize) {
420 return totalSize - chunk_header_size();
421 }
422
425 static Chunk* create(
426 const World& wld, const ComponentCache& cc, //
427 uint32_t chunkIndex, uint16_t capacity, uint8_t cntEntities, //
428 uint16_t dataBytes, uint32_t& worldVersion,
429 // data offsets
430 const ChunkDataOffsets& offsets,
431 // component entities
432 const Entity* ids,
433 // resolved component storage items
434 const ComponentCacheItem* const* pItems,
435 // component offsets
436 const ChunkDataOffset* compOffs) {
437 const auto totalBytes = chunk_total_bytes(dataBytes);
438#if GAIA_ECS_CHUNK_ALLOCATOR
439 auto* pChunk = (Chunk*)ChunkAllocator::get().alloc(totalBytes);
440 (void)new (pChunk) Chunk(wld, cc, chunkIndex, capacity, worldVersion);
441 [[maybe_unused]] const auto* pAllocEnd = (const uint8_t*)pChunk - MemoryBlockUsableOffset + totalBytes;
442#else
443 GAIA_ASSERT(totalBytes <= MaxMemoryBlockSize);
444 const auto sizeType = mem_block_size_type(totalBytes);
445 const auto allocSize = mem_block_size(sizeType);
446 auto* pChunkMem = mem::AllocHelper::alloc<uint8_t>(allocSize);
447 std::memset(pChunkMem, 0, allocSize);
448 auto* pChunk = new (pChunkMem) Chunk(wld, cc, chunkIndex, capacity, worldVersion);
449 [[maybe_unused]] const auto* pAllocEnd = pChunkMem + totalBytes;
450#endif
451 // The data area starts at the first MemoryBlockAlignment boundary inside m_data. It is computed only
452 // here, init() caches every pointer derived from it.
453 auto* pDataArea = (uint8_t*)mem::align<MemoryBlockAlignment>((uintptr_t)&pChunk->m_data[0]);
454 // The aligned data area has to fit the space chunk_header_size() reserved for it
455 GAIA_ASSERT(pDataArea + dataBytes <= pAllocEnd);
456
457 pChunk->init(pDataArea, (uint32_t)cntEntities, ids, pItems, offsets, compOffs);
458 return pChunk;
459 }
460
463 static void free(Chunk* pChunk) {
464 GAIA_ASSERT(pChunk != nullptr);
465 GAIA_ASSERT(!pChunk->dead());
466
467 // Mark as dead
468 pChunk->die();
469
470 // Call destructors for components that need it
471 pChunk->call_all_dtors();
472
473 pChunk->~Chunk();
474#if GAIA_ECS_CHUNK_ALLOCATOR
475 ChunkAllocator::get().free(pChunk);
476#else
477 mem::AllocHelper::free((uint8_t*)pChunk);
478#endif
479 }
480
483 void save(ser::serializer& s) const {
484 s.save(m_header.count);
485 if (m_header.count == 0)
486 return;
487
488 s.save(m_header.countEnabled);
489
490 const uint16_t dead = m_header.dead;
491 const uint16_t lifespanCountdown = m_header.lifespanCountdown;
492 s.save(dead);
493 s.save(lifespanCountdown);
494
495 const auto cnt = (uint32_t)m_header.count;
496 const auto cap = (uint32_t)m_header.capacity;
497
498 // Store entity data
499 {
500 const auto* pData = m_records.pEntities;
501 GAIA_FOR(cnt) s.save(pData[i]);
502 }
503
504 // Store component data
505 {
506 for (const auto& rec: comp_rec_view()) {
507 // Skip the component if there's no size associated with it
508 if (!component_uses_table_storage(rec.comp))
509 continue;
510
511 rec.pItem->save(s, rec.pData, 0, cnt, cap);
512 }
513 }
514 }
515
519 uint16_t prevCount = m_header.count;
520 s.load(m_header.count);
521 if (m_header.count == 0)
522 return;
523
524 s.load(m_header.countEnabled);
525
526 uint16_t dead = 0;
527 uint16_t lifespanCountdown = 0;
528 s.load(dead);
529 s.load(lifespanCountdown);
530 m_header.dead = dead != 0;
531 m_header.lifespanCountdown = lifespanCountdown;
532
533 const auto cnt = (uint32_t)m_header.count;
534 const auto cap = (uint32_t)m_header.capacity;
535
536 // Load entity data
537 {
538 GAIA_FOR(cnt) {
539 Entity e;
540 s.load(e);
541 entity_view_mut()[i] = e;
542 }
543 }
544
545 // Load component data. Call constructors first as necessary.
546 call_ctors(prevCount, cnt);
547 {
548 for (const auto& rec: comp_rec_view()) {
549 // Skip the component if there's no size associated with it
550 if (!component_uses_table_storage(rec.comp))
551 continue;
552
553 rec.pItem->load(s, rec.pData, 0, cnt, cap);
554 }
555 }
556 }
557
561 // Should never be called over an empty chunk
562 GAIA_ASSERT(!empty());
563
564#if GAIA_ASSERT_ENABLED
565 // Invalidate the entity in chunk data
566 entity_view_mut()[m_header.count - 1] = EntityBad;
567#endif
568
569 --m_header.count;
570 }
571
574 ::gaia::ecs::update_version(m_header.worldVersion);
575 update_world_version();
576 update_entity_order_version();
577 }
578
585 template <typename T>
586 GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const {
587 using U = typename actual_type_t<T>::Type;
588
589 // Always consider full range for SoA
590 if constexpr (mem::is_soa_layout_v<U>)
591 return mem::auto_view_policy_get<U>{view_inter<T>(0, capacity())};
592 else
593 return mem::auto_view_policy_get<U>{view_inter<T>(from, to)};
594 }
595
599 template <typename T>
600 GAIA_NODISCARD decltype(auto) view() const {
601 return view<T>(0, m_header.count);
602 }
603
609 template <typename T>
610 GAIA_NODISCARD decltype(auto) view_raw(const void* ptr, uint32_t size) const {
611 using U = typename actual_type_t<T>::Type;
612 return mem::auto_view_policy_get<U>{std::span{(const uint8_t*)ptr, size}};
613 }
614
621 template <typename T>
622 GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to) {
623 using U = typename actual_type_t<T>::Type;
624 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
625
626 // Always consider full range for SoA
627 if constexpr (mem::is_soa_layout_v<U>)
628 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(0, capacity())};
629 else
630 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(from, to)};
631 }
632
636 template <typename T>
637 GAIA_NODISCARD decltype(auto) view_mut() {
638 return view_mut<T>(0, m_header.count);
639 }
640
646 template <typename T>
647 GAIA_NODISCARD decltype(auto) view_mut_raw(void* ptr, uint32_t size) const {
648 using U = typename actual_type_t<T>::Type;
649 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
650
651 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
652 }
653
661 template <typename T>
662 GAIA_NODISCARD decltype(auto) sview_mut(uint16_t from, uint16_t to) {
663 using U = typename actual_type_t<T>::Type;
664 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
665
666 // Always consider full range for SoA
667 if constexpr (mem::is_soa_layout_v<U>)
668 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(0, capacity())};
669 else
670 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(from, to)};
671 }
672
678 template <typename T>
679 GAIA_NODISCARD decltype(auto) sview_mut_raw(void* ptr, uint32_t size) const {
680 using U = typename actual_type_t<T>::Type;
681 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
682
683 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
684 }
685
689 template <typename T>
690 GAIA_NODISCARD decltype(auto) sview_mut() {
691 return sview_mut<T>(0, m_header.count);
692 }
693
697 template <typename T, bool TriggerSetHooks>
698 GAIA_FORCEINLINE void modify() {
699 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "mod can't be used to modify Entity");
700
701 if constexpr (is_pair<T>::value) {
702 using TT = typename T::type;
703 using U = typename component_type_t<TT>::Type;
704 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
705
706 const auto rel = m_header.cc->get<typename T::rel>().entity;
707 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
708 const auto compIdx = comp_idx((Entity)Pair(rel, tgt));
709
710 // Update version number if necessary so we know RW access was used on the chunk
711 update_world_version(compIdx);
712
713#if GAIA_ENABLE_SET_HOOKS
714 if constexpr (TriggerSetHooks) {
715 const auto& rec = m_records.pRecords[compIdx];
716 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
717 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
718 }
719#endif
720 } else {
721 using U = typename component_type_t<T>::Type;
722 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
723
724 const auto comp = m_header.cc->get<T>().entity;
725 const auto compIdx = comp_idx(comp);
726
727 // Update version number if necessary so we know RW access was used on the chunk
728 update_world_version(compIdx);
729
730#if GAIA_ENABLE_SET_HOOKS
731 if constexpr (TriggerSetHooks) {
732 const auto& rec = m_records.pRecords[compIdx];
733 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
734 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
735 }
736#endif
737 }
738 }
739
747 template <typename T>
748 GAIA_NODISCARD decltype(auto) view_auto(uint16_t from, uint16_t to) {
749 using UOriginal = typename actual_type_t<T>::TypeOriginal;
750 if constexpr (core::is_mut_v<UOriginal>)
751 return view_mut<T>(from, to);
752 else
753 return view<T>(from, to);
754 }
755
759 template <typename T>
760 GAIA_NODISCARD decltype(auto) view_auto() {
761 return view_auto<T>(0, m_header.count);
762 }
763
772 template <typename T>
773 GAIA_NODISCARD decltype(auto) sview_auto(uint16_t from, uint16_t to) {
774 using UOriginal = typename actual_type_t<T>::TypeOriginal;
775 if constexpr (core::is_mut_v<UOriginal>)
776 return sview_mut<T>(from, to);
777 else
778 return view<T>(from, to);
779 }
780
784 template <typename T>
785 GAIA_NODISCARD decltype(auto) sview_auto() {
786 return sview_auto<T>(0, m_header.count);
787 }
788
791 GAIA_NODISCARD EntitySpan entity_view() const {
792 return {(const Entity*)m_records.pEntities, m_header.count};
793 }
794
797 GAIA_NODISCARD World& world() {
798 return *const_cast<World*>(m_header.world);
799 }
800
803 GAIA_NODISCARD const World& world() const {
804 return *m_header.world;
805 }
806
809 GAIA_NODISCARD EntitySpan ids_view() const {
810 return {(const Entity*)m_records.pCompEntities, m_header.cntEntities};
811 }
812
815 GAIA_NODISCARD std::span<const ComponentRecord> comp_rec_view() const {
816 return {m_records.pRecords, m_header.cntEntities};
817 }
818
822 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx) {
823 const auto& rec = m_records.pRecords[compIdx];
824 return rec.pData;
825 }
826
831 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx, uint32_t offset) {
832 const auto& rec = m_records.pRecords[compIdx];
833 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
834 }
835
839 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx) const {
840 const auto& rec = m_records.pRecords[compIdx];
841 return rec.pData;
842 }
843
848 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx, uint32_t offset) const {
849 const auto& rec = m_records.pRecords[compIdx];
850 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
851 }
852
855 GAIA_NODISCARD uint16_t add_entity(Entity entity) {
856 const auto row = m_header.count++;
857
858 // Zero after increase of value means an overflow!
859 GAIA_ASSERT(m_header.count != 0);
860
861 ++m_header.countEnabled;
862 entity_view_mut()[row] = entity;
863
864 return row;
865 }
866
871 static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers& recs) {
872 GAIA_PROF_SCOPE(Chunk::copy_entity_data);
873
874 auto& srcEntityContainer = recs[srcEntity];
875 auto* pSrcChunk = srcEntityContainer.pChunk;
876
877 auto& dstEntityContainer = recs[dstEntity];
878 auto* pDstChunk = dstEntityContainer.pChunk;
879
880 GAIA_ASSERT(srcEntityContainer.pArchetype == dstEntityContainer.pArchetype);
881
882 auto srcRecs = pSrcChunk->comp_rec_view();
883
884 // Copy component data from reference entity to our new entity.
885 GAIA_FOR(pSrcChunk->m_header.cntEntities) {
886 const auto& rec = srcRecs[i];
887 if (!component_uses_table_storage(rec.comp))
888 continue;
889
890 const auto* pSrc = (const void*)pSrcChunk->comp_ptr_mut(i);
891 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
892 rec.pItem->copy(
893 pDst, pSrc, dstEntityContainer.row, srcEntityContainer.row, pDstChunk->capacity(), pSrcChunk->capacity());
894 }
895 }
896
904 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
905 GAIA_PROF_SCOPE(Chunk::copy_entity_data_n_same_chunk);
906
907 GAIA_ASSERT(pSrcChunk != nullptr);
908 GAIA_ASSERT(pDstChunk != nullptr);
909 GAIA_ASSERT(srcRow < pSrcChunk->size());
910 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
911 GAIA_ASSERT(pSrcChunk->ids_view().size() == pDstChunk->ids_view().size());
912
913 auto srcRecs = pSrcChunk->comp_rec_view();
914
915 // Copy component data from the reference entity to all newly allocated rows.
916 GAIA_FOR(pSrcChunk->m_header.cntEntities) {
917 const auto& rec = srcRecs[i];
918 if (!component_uses_table_storage(rec.comp))
919 continue;
920
921 const auto* pSrc = (const void*)pSrcChunk->comp_ptr(i);
922 GAIA_FOR_(dstCount, rowOffset) {
923 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
924 rec.pItem->copy(pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
925 }
926 }
927 }
928
936 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
937 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data_n);
938
939 GAIA_ASSERT(pSrcChunk != nullptr);
940 GAIA_ASSERT(pDstChunk != nullptr);
941 GAIA_ASSERT(srcRow < pSrcChunk->size());
942 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
943
944 auto srcIds = pSrcChunk->ids_view();
945 auto dstIds = pDstChunk->ids_view();
946 auto dstRecs = pDstChunk->comp_rec_view();
947
948 uint32_t i = 0;
949 uint32_t j = 0;
950 while (i < pSrcChunk->m_header.cntEntities && j < pDstChunk->m_header.cntEntities) {
951 const auto oldId = srcIds[i];
952 const auto newId = dstIds[j];
953
954 if (oldId == newId) {
955 const auto& rec = dstRecs[j];
956 if (component_uses_table_storage(rec.comp)) {
957 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
958 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
959 GAIA_FOR_(dstCount, rowOffset) {
960 rec.pItem->ctor_copy(
961 pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
962 }
963 }
964
965 ++i;
966 ++j;
967 } else if (SortComponentCond{}.operator()(oldId, newId)) {
968 ++i;
969 } else {
970 const auto& rec = dstRecs[j];
971 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr && //
972 component_uses_table_storage(rec.comp)) {
973 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
974 rec.pItem->func_ctor(pDst, dstCount);
975 }
976
977 ++j;
978 }
979 }
980
981 for (; j < pDstChunk->m_header.cntEntities; ++j) {
982 const auto& rec = dstRecs[j];
983 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr && //
984 component_uses_table_storage(rec.comp)) {
985 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
986 rec.pItem->func_ctor(pDst, dstCount);
987 }
988 }
989 }
990
995 void move_entity_data(Entity entity, uint16_t row, EntityContainers& recs) {
996 GAIA_PROF_SCOPE(Chunk::move_entity_data);
997
998 auto& ec = recs[entity];
999 auto* pSrcChunk = ec.pChunk;
1000 auto srcRecs = pSrcChunk->comp_rec_view();
1001
1002 // Copy component data from reference entity to our new entity.
1003 GAIA_FOR(pSrcChunk->m_header.cntEntities) {
1004 const auto& rec = srcRecs[i];
1005 if (!component_uses_table_storage(rec.comp))
1006 continue;
1007
1008 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1009 auto* pDst = (void*)comp_ptr_mut(i);
1010 rec.pItem->ctor_move(pDst, pSrc, row, ec.row, capacity(), pSrcChunk->capacity());
1011 }
1012 }
1013
1019 static void copy_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1020 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data);
1021
1022 GAIA_ASSERT(pSrcChunk != nullptr);
1023 GAIA_ASSERT(pDstChunk != nullptr);
1024 GAIA_ASSERT(srcRow < pSrcChunk->size());
1025 GAIA_ASSERT(dstRow < pDstChunk->size());
1026
1027 auto srcIds = pSrcChunk->ids_view();
1028 auto dstIds = pDstChunk->ids_view();
1029 auto dstRecs = pDstChunk->comp_rec_view();
1030
1031 // Find intersection of the two component lists.
1032 // Arrays are sorted so we can do linear intersection lookup.
1033 // Call constructor on each match.
1034 {
1035 uint32_t i = 0;
1036 uint32_t j = 0;
1037 while (i < pSrcChunk->m_header.cntEntities && j < pDstChunk->m_header.cntEntities) {
1038 const auto oldId = srcIds[i];
1039 const auto newId = dstIds[j];
1040
1041 if (oldId == newId) {
1042 const auto& rec = dstRecs[j];
1043 if (component_uses_table_storage(rec.comp)) {
1044 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1045 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1046 rec.pItem->ctor_copy(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1047 }
1048
1049 ++i;
1050 ++j;
1051 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1052 ++i;
1053 } else {
1054 // No match with the old chunk. Construct the component
1055 const auto& rec = dstRecs[j];
1056 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr && //
1057 component_uses_table_storage(rec.comp)) {
1058 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1059 rec.pItem->func_ctor(pDst, 1);
1060 }
1061
1062 ++j;
1063 }
1064 }
1065
1066 // Initialize remaining destination columns.
1067 for (; j < pDstChunk->m_header.cntEntities; ++j) {
1068 const auto& rec = dstRecs[j];
1069 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr && //
1070 component_uses_table_storage(rec.comp)) {
1071 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1072 rec.pItem->func_ctor(pDst, 1);
1073 }
1074 }
1075 }
1076 }
1077
1083 static void move_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1084 GAIA_PROF_SCOPE(Chunk::move_foreign_entity_data);
1085
1086 GAIA_ASSERT(pSrcChunk != nullptr);
1087 GAIA_ASSERT(pDstChunk != nullptr);
1088 GAIA_ASSERT(srcRow < pSrcChunk->size());
1089 GAIA_ASSERT(dstRow < pDstChunk->size());
1090
1091 auto srcIds = pSrcChunk->ids_view();
1092 auto dstIds = pDstChunk->ids_view();
1093 auto dstRecs = pDstChunk->comp_rec_view();
1094
1095 // Find intersection of the two component lists.
1096 // Arrays are sorted so we can do linear intersection lookup.
1097 // Call constructor on each match.
1098 {
1099 uint32_t i = 0;
1100 uint32_t j = 0;
1101 while (i < pSrcChunk->m_header.cntEntities && j < pDstChunk->m_header.cntEntities) {
1102 const auto oldId = srcIds[i];
1103 const auto newId = dstIds[j];
1104
1105 if (oldId == newId) {
1106 const auto& rec = dstRecs[j];
1107 if (component_uses_table_storage(rec.comp)) {
1108 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1109 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1110 rec.pItem->ctor_move(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1111 }
1112
1113 ++i;
1114 ++j;
1115 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1116 ++i;
1117 } else {
1118 // No match with the old chunk. Construct the component
1119 const auto& rec = dstRecs[j];
1120 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr && //
1121 component_uses_table_storage(rec.comp)) {
1122 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1123 rec.pItem->func_ctor(pDst, 1);
1124 }
1125
1126 ++j;
1127 }
1128 }
1129
1130 // Initialize remaining destination columns.
1131 for (; j < pDstChunk->m_header.cntEntities; ++j) {
1132 const auto& rec = dstRecs[j];
1133 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr && //
1134 component_uses_table_storage(rec.comp)) {
1135 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1136 rec.pItem->func_ctor(pDst, 1);
1137 }
1138 }
1139 }
1140 }
1141
1148 void remove_entity_inter(uint16_t row, EntityContainers& recs) {
1149 GAIA_PROF_SCOPE(Chunk::remove_entity_inter);
1150
1151 const uint16_t rowA = row;
1152 const uint16_t rowB = m_header.count - 1;
1153 // The "rowA" entity is the one we are going to destroy so it needs to precede the "rowB"
1154 GAIA_ASSERT(rowA <= rowB);
1155
1156 // To move anything, we need at least 2 entities
1157 if GAIA_LIKELY (rowA < rowB) {
1158 GAIA_ASSERT(m_header.count > 1);
1159
1160 auto ev = entity_view_mut();
1161
1162 // Update entity data
1163 const auto entityB = ev[rowB];
1164 auto& ecB = recs[entityB];
1165#if GAIA_ASSERT_ENABLED
1166 const auto entityA = ev[rowA];
1167 auto& ecA = recs[entityA];
1168
1169 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1170 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1171#endif
1172
1173 ev[rowA] = entityB;
1174
1175 // Move component data from entityB to entityA
1176 auto recView = comp_rec_view();
1177 GAIA_FOR(m_header.cntEntities) {
1178 const auto& rec = recView[i];
1179 if (!component_uses_table_storage(rec.comp))
1180 continue;
1181
1182 auto* pSrc = (void*)comp_ptr_mut(i);
1183 rec.pItem->move(pSrc, pSrc, rowA, rowB, capacity(), capacity());
1184
1185 pSrc = (void*)comp_ptr_mut(i, rowB);
1186 rec.pItem->dtor(pSrc);
1187 }
1188
1189 // Entity has been replaced with the last one in our chunk. Update its container record.
1190 ecB.row = rowA;
1191 ecB.pEntity = &ev[rowA];
1192 } else if (m_header.hasAnyCustomDtor) {
1193 // This is the last entity in the chunk so simply destroy its data
1194 auto recView = comp_rec_view();
1195 GAIA_FOR(m_header.cntEntities) {
1196 const auto& rec = recView[i];
1197 if (!component_uses_table_storage(rec.comp))
1198 continue;
1199
1200 auto* pSrc = (void*)comp_ptr_mut(i, rowA);
1201 rec.pItem->dtor(pSrc);
1202 }
1203 }
1204 }
1205
1212 void remove_entity(uint16_t row, EntityContainers& recs) {
1213 if GAIA_UNLIKELY (m_header.count == 0)
1214 return;
1215
1216 GAIA_PROF_SCOPE(Chunk::remove_entity);
1217
1218 if (enabled(row)) {
1219 // Entity was previously enabled. Swap with the last entity
1220 remove_entity_inter(row, recs);
1221 // At this point the last entity is no longer valid so remove it
1222 remove_last_entity();
1223 --m_header.countEnabled;
1224 } else {
1225 // Entity was previously disabled. Swap with the last disabled entity
1226 const uint16_t pivot = size_disabled() - 1;
1227 swap_chunk_entities(row, pivot, recs);
1228 // Once swapped, try to swap with the last (enabled) entity in the chunk.
1229 remove_entity_inter(pivot, recs);
1230 --m_header.rowFirstEnabledEntity;
1231 // At this point the last entity is no longer valid so remove it
1232 remove_last_entity();
1233 }
1234 }
1235
1243 void swap_chunk_entities(uint16_t rowA, uint16_t rowB, EntityContainers& recs) {
1244 // If there are at least two different entities inside to swap
1245 if GAIA_UNLIKELY (m_header.count <= 1 || rowA == rowB)
1246 return;
1247
1248 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1249
1250 // Update entity data
1251 auto ev = entity_view_mut();
1252 const auto entityA = ev[rowA];
1253 const auto entityB = ev[rowB];
1254
1255 auto& ecA = recs[entityA];
1256 auto& ecB = recs[entityB];
1257 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1258 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1259
1260 ev[rowA] = entityB;
1261 ev[rowB] = entityA;
1262
1263 // Swap component data
1264 auto recView = comp_rec_view();
1265 GAIA_FOR(m_header.cntEntities) {
1266 const auto& rec = recView[i];
1267 if (!component_uses_table_storage(rec.comp))
1268 continue;
1269
1270 GAIA_ASSERT(rec.pData == comp_ptr_mut(i));
1271 rec.pItem->swap(rec.pData, rec.pData, rowA, rowB, capacity(), capacity());
1272 }
1273
1274 // Update indices in entity container.
1275 ecA.row = rowB;
1276 ecB.row = rowA;
1277 ecA.pEntity = &ev[rowB];
1278 ecB.pEntity = &ev[rowA];
1279 }
1280
1287 static void swap_chunk_entities(World& world, Entity entityA, Entity entityB) {
1288 // Don't swap if the two entities are the same
1289 if GAIA_UNLIKELY (entityA == entityB)
1290 return;
1291
1292 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1293
1294 auto& ecA = fetch_mut(world, entityA);
1295 auto& ecB = fetch_mut(world, entityB);
1296
1297 // Make sure the two entities are in the same archetype
1298 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1299 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1300
1301 auto* pChunkA = ecA.pChunk;
1302 auto* pChunkB = ecB.pChunk;
1303
1304 // Swap entities in the entity data part
1305 pChunkA->entity_view_mut()[ecA.row] = entityB;
1306 pChunkB->entity_view_mut()[ecB.row] = entityA;
1307
1308 // Swap component data
1309 auto recViewA = pChunkA->comp_rec_view();
1310 GAIA_FOR(pChunkA->m_header.cntEntities) {
1311 const auto& recA = recViewA[i];
1312 if (!component_uses_table_storage(recA.comp))
1313 continue;
1314
1315 auto* pDataA = pChunkA->comp_rec_view()[i].pData;
1316 auto* pDataB = pChunkB->comp_rec_view()[i].pData;
1317 recA.pItem->swap(
1318 // Data pointers
1319 pDataA, pDataB,
1320 // Rows
1321 ecA.row, ecB.row,
1322 // Chunk capacities
1323 pChunkA->capacity(), pChunkA->capacity() //
1324 );
1325 }
1326
1327 // Update indices and chunks in entity container.
1328 core::swap(ecA.row, ecB.row);
1329 core::swap(ecA.pChunk, ecB.pChunk);
1330 ecA.pEntity = &ecA.pChunk->entity_view()[ecA.row];
1331 ecB.pEntity = &ecB.pChunk->entity_view()[ecB.row];
1332 }
1333
1338 void enable_entity(uint16_t row, bool enableEntity, EntityContainers& recs) {
1339 GAIA_ASSERT(row < m_header.count && "Entity chunk row out of bounds!");
1340
1341 if (enableEntity) {
1342 // Nothing to enable if there are no disabled entities
1343 if (!m_header.has_disabled_entities())
1344 return;
1345 // Trying to enable an already enabled entity
1346 if (enabled(row))
1347 return;
1348 // Try swapping our entity with the last disabled one
1349 const auto entity = entity_view()[row];
1350 swap_chunk_entities(--m_header.rowFirstEnabledEntity, row, recs);
1351 recs[entity].data.dis = 0;
1352 ++m_header.countEnabled;
1353 } else {
1354 // Nothing to disable if there are no enabled entities
1355 if (!m_header.has_enabled_entities())
1356 return;
1357 // Trying to disable an already disabled entity
1358 if (!enabled(row))
1359 return;
1360 // Try swapping our entity with the last one in our chunk
1361 const auto entity = entity_view()[row];
1362 swap_chunk_entities(m_header.rowFirstEnabledEntity++, row, recs);
1363 recs[entity].data.dis = 1;
1364 --m_header.countEnabled;
1365 }
1366 }
1367
1371 bool enabled(uint16_t row) const {
1372 GAIA_ASSERT(m_header.count > 0);
1373
1374 return row >= (uint16_t)m_header.rowFirstEnabledEntity;
1375 }
1376
1377 //----------------------------------------------------------------------
1378 // Component handling
1379 //----------------------------------------------------------------------
1380
1385 void call_ctor(uint32_t entIdx, uint32_t compIdx, const ComponentCacheItem& item) {
1386 if (item.func_ctor == nullptr || !component_uses_table_storage(item.comp))
1387 return;
1388
1389 GAIA_PROF_SCOPE(Chunk::call_ctor);
1390
1391 auto* pSrc = (void*)comp_ptr_mut(compIdx, entIdx);
1392 item.func_ctor(pSrc, 1);
1393 }
1394
1398 void call_ctors(uint32_t entIdx, uint32_t entCnt) {
1399 if (!m_header.hasAnyCustomCtor)
1400 return;
1401
1402 GAIA_PROF_SCOPE(Chunk::call_ctors);
1403
1404 auto recs = comp_rec_view();
1405 GAIA_FOR(m_header.cntEntities) {
1406 const auto& rec = recs[i];
1407 if (!component_uses_table_storage(rec.comp))
1408 continue;
1409
1410 const auto* pItem = rec.pItem;
1411 if (pItem == nullptr || pItem->func_ctor == nullptr)
1412 continue;
1413
1414 auto* pSrc = (void*)comp_ptr_mut(i, entIdx);
1415 pItem->func_ctor(pSrc, entCnt);
1416 }
1417 }
1418
1421 if (!m_header.hasAnyCustomDtor)
1422 return;
1423
1424 GAIA_PROF_SCOPE(Chunk::call_all_dtors);
1425
1426 auto recs = comp_rec_view();
1427 const auto recs_cnt = recs.size();
1428 GAIA_FOR(recs_cnt) {
1429 const auto& rec = recs[i];
1430 if (!component_uses_table_storage(rec.comp))
1431 continue;
1432
1433 const auto* pItem = rec.pItem;
1434 if (pItem == nullptr || pItem->func_dtor == nullptr)
1435 continue;
1436
1437 auto* pSrc = (void*)comp_ptr_mut(i, 0);
1438 const auto cnt = m_header.count;
1439 pItem->func_dtor(pSrc, cnt);
1440 }
1441 }
1442
1443 //----------------------------------------------------------------------
1444 // Check component presence
1445 //----------------------------------------------------------------------
1446
1450 GAIA_NODISCARD bool has(Entity entity) const {
1451 auto ids = ids_view();
1452 return core::has(ids, entity);
1453 }
1454
1458 template <typename T>
1459 GAIA_NODISCARD bool has() const {
1460 if constexpr (is_pair<T>::value) {
1461 const auto rel = m_header.cc->get<typename T::rel>().entity;
1462 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1463 return has((Entity)Pair(rel, tgt));
1464 } else {
1465 const auto* pComp = m_header.cc->find<T>();
1466 return pComp != nullptr && has(pComp->entity);
1467 }
1468 }
1469
1470 //----------------------------------------------------------------------
1471 // Set component data
1472 //----------------------------------------------------------------------
1473
1479 template <typename T>
1480 decltype(auto) set(uint16_t row) {
1481 verify_comp<T>();
1482
1483 // Update the world version
1484 ::gaia::ecs::update_version(m_header.worldVersion);
1485
1486 GAIA_ASSERT(row < m_header.capacity);
1487 world_notify_on_set(*const_cast<World*>(m_header.world), comp_entity<T>(), *this, row, (uint16_t)(row + 1));
1488 return view_mut<T>()[row];
1489 }
1490
1496 template <typename T>
1497 decltype(auto) set_idx(uint16_t row, uint32_t compIdx) {
1498 verify_comp<T>();
1499
1500 // Update the world version
1501 ::gaia::ecs::update_version(m_header.worldVersion);
1502
1503 world_notify_on_set(
1504 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, row, (uint16_t)(row + 1));
1505 return comp_mut_idx<T, true>(row, compIdx);
1506 }
1507
1513 template <typename T>
1514 decltype(auto) set(uint16_t row, Entity type) {
1515 const uint32_t compIdx = comp_idx(type);
1516 GAIA_ASSERT(m_records.pRecords[compIdx].pItem != nullptr);
1517
1518 // Update the world version
1519 ::gaia::ecs::update_version(m_header.worldVersion);
1520
1521 GAIA_ASSERT(row < m_header.capacity);
1522 world_notify_on_set(*const_cast<World*>(m_header.world), type, *this, row, (uint16_t)(row + 1));
1523 return comp_mut_idx<T, true>(row, compIdx);
1524 }
1525
1532 template <typename T>
1533 decltype(auto) sset(uint16_t row) {
1534 GAIA_ASSERT(row < m_header.capacity);
1535 return sview_mut<T>()[row];
1536 }
1537
1541 template <typename T>
1542 decltype(auto) sset_idx(uint16_t row, uint32_t compIdx) {
1543 verify_comp<T>();
1544
1545 return comp_mut_idx<T, false>(row, compIdx);
1546 }
1547
1555 template <typename T>
1556 decltype(auto) sset(uint16_t row, Entity type) {
1557 static_assert(core::is_raw_v<T>);
1558
1559 const uint32_t compIdx = comp_idx(type);
1560 GAIA_ASSERT(m_records.pRecords[compIdx].pItem != nullptr);
1561
1562 GAIA_ASSERT(row < m_header.capacity);
1563 return comp_mut_idx<T, false>(row, compIdx);
1564 }
1565
1566 //----------------------------------------------------------------------
1567 // Read component data
1568 //----------------------------------------------------------------------
1569
1576 template <typename T>
1577 GAIA_NODISCARD decltype(auto) get(uint16_t row) const {
1578 return comp_inter<T>(row);
1579 }
1580
1586 template <typename T>
1587 GAIA_NODISCARD decltype(auto) get_idx(uint16_t row, uint32_t compIdx) const {
1588 return comp_inter_idx<T>(row, compIdx);
1589 }
1590
1597 template <typename T>
1598 GAIA_NODISCARD decltype(auto) get(uint16_t row, Entity type) const {
1599 GAIA_ASSERT(row < m_header.count);
1600 const uint32_t compIdx = comp_idx(type);
1601 GAIA_ASSERT(m_records.pRecords[compIdx].pItem != nullptr);
1602 return comp_inter_idx<T>(row, compIdx);
1603 }
1604
1608 template <typename T>
1609 GAIA_NODISCARD Entity comp_entity() const {
1610 if constexpr (is_pair<T>::value) {
1611 const auto rel = m_header.cc->get<typename T::rel>().entity;
1612 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1613 return (Entity)Pair(rel, tgt);
1614 } else {
1615 return m_header.cc->get<T>().entity;
1616 }
1617 }
1618
1623 GAIA_NODISCARD uint32_t comp_idx(Entity entity) const {
1624 return ecs::comp_idx<ChunkHeader::MAX_COMPONENTS>(m_records.pCompEntities, entity);
1625 }
1626
1632 GAIA_NODISCARD uint32_t comp_idx(Entity entity, uint32_t offset) const {
1633 return ecs::comp_idx({m_records.pCompEntities + offset, m_header.count - offset}, entity);
1634 }
1635
1636 //----------------------------------------------------------------------
1637
1639 void set_idx(uint32_t value) {
1640 m_header.index = value;
1641 }
1642
1645 GAIA_NODISCARD uint32_t idx() const {
1646 return m_header.index;
1647 }
1648
1651 GAIA_NODISCARD bool has_enabled_entities() const {
1652 return m_header.has_enabled_entities();
1653 }
1654
1657 GAIA_NODISCARD bool has_disabled_entities() const {
1658 return m_header.has_disabled_entities();
1659 }
1660
1663 GAIA_NODISCARD bool dying() const {
1664 return m_header.lifespanCountdown > 0;
1665 }
1666
1669 GAIA_NODISCARD bool queued_for_deletion() const {
1670 return m_header.deleteQueueIndex != BadIndex;
1671 }
1672
1675 GAIA_NODISCARD uint32_t delete_queue_index() const {
1676 return m_header.deleteQueueIndex;
1677 }
1678
1680 void delete_queue_index(uint32_t idx) {
1681 m_header.deleteQueueIndex = idx;
1682 }
1683
1686 m_header.deleteQueueIndex = BadIndex;
1687 }
1688
1690 void die() {
1691 m_header.dead = 1;
1692 }
1693
1696 GAIA_NODISCARD bool dead() const {
1697 return m_header.dead == 1;
1698 }
1699
1702 GAIA_ASSERT(!dead());
1703 GAIA_ASSERT(!queued_for_deletion());
1704 m_header.lifespanCountdown = ChunkHeader::MAX_CHUNK_LIFESPAN;
1705 }
1706
1708 void revive() {
1709 GAIA_ASSERT(!dead());
1710 m_header.lifespanCountdown = 0;
1711 clear_delete_queue_index();
1712 }
1713
1717 GAIA_ASSERT(dying());
1718 --m_header.lifespanCountdown;
1719 return dying();
1720 }
1721
1724 GAIA_NODISCARD bool full() const {
1725 return m_header.count >= m_header.capacity;
1726 }
1727
1730 GAIA_NODISCARD bool is_semi() const {
1731 // We want the chunk filled to at least 75% before considering it semi-full
1732 constexpr float Threshold = 0.75f;
1733 return ((float)m_header.count / (float)m_header.capacity) < Threshold;
1734 }
1735
1738 GAIA_NODISCARD uint16_t size() const {
1739 return m_header.count;
1740 }
1741
1744 GAIA_NODISCARD bool empty() const {
1745 return m_header.count == 0;
1746 }
1747
1750 GAIA_NODISCARD uint16_t size_enabled() const {
1751 return m_header.countEnabled;
1752 }
1753
1756 GAIA_NODISCARD uint16_t size_disabled() const {
1757 return (uint16_t)m_header.rowFirstEnabledEntity;
1758 }
1759
1762 GAIA_NODISCARD uint16_t capacity() const {
1763 return m_header.capacity;
1764 }
1765
1772 GAIA_NODISCARD bool changed(uint32_t requiredVersion) const {
1773 const auto* versions = m_records.pVersions;
1774 const auto changeVersion = versions[0];
1775 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1776 }
1777
1782 GAIA_NODISCARD bool changed(uint32_t requiredVersion, uint32_t compIdx) const {
1783 const auto* versions = m_records.pVersions;
1784 // Do +1 because index 0 is reserved for the entity version number.
1785 const auto changeVersion = versions[compIdx + 1];
1786 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1787 }
1788
1793 GAIA_NODISCARD bool entity_order_changed(uint32_t requiredVersion) const {
1794 return ::gaia::ecs::version_changed(m_header.entityOrderVersion, requiredVersion);
1795 }
1796
1798 GAIA_FORCEINLINE void update_world_version(uint32_t compIdx) {
1799 auto versions = comp_version_view_mut();
1800 // Only the component's own slot is stamped. The chunk-wide slot at index 0 tracks structural
1801 // changes. Parallel systems may write different components of one chunk at the same time,
1802 // so a write must not touch state shared with other components.
1803 // Do +1 because index 0 is reserved for the entity version number.
1804 versions[compIdx + 1] = m_header.worldVersion;
1805 // Sorted queries keyed by this component can invalidate their cached order immediately.
1806 world_invalidate_sorted_queries_for_entity(
1807 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx]);
1808 }
1809
1811 GAIA_FORCEINLINE void update_entity_order_version() {
1812 m_header.entityOrderVersion = m_header.worldVersion;
1813 // Row-order changes invalidate cached sorted slices regardless of sort key.
1814 world_invalidate_sorted_queries(*const_cast<World*>(m_header.world));
1815 }
1816
1818 GAIA_FORCEINLINE void update_world_version() {
1819 // Edit the version pointer directly. The first elements is always the entity version.
1820 // This area of memory is always present.
1821 auto* versions = m_records.pVersions;
1822 // We update the version of the entity only. If this one changes,
1823 // all other components are considered changed as well.
1824 versions[0] = m_header.worldVersion;
1825 }
1826
1828 GAIA_FORCEINLINE void update_world_version_init() {
1829 auto* versions = m_records.pVersions;
1830 // We update the version of the entity and all components to match the world version.
1831 versions[0] = m_header.worldVersion;
1832 GAIA_FOR(m_header.cntEntities) versions[1 + i] = m_header.worldVersion;
1833 m_header.entityOrderVersion = m_header.worldVersion;
1834 }
1835
1837 void diag() const {
1838 GAIA_LOG_N(
1839 " Chunk #%04u, entities:%u/%u, lifespanCountdown:%u", m_header.index, m_header.count, m_header.capacity,
1840 m_header.lifespanCountdown);
1841 }
1842 };
1843 } // namespace ecs
1844} // namespace gaia
Array of elements of type.
Definition darray_ext_impl.h:31
Fixed-capacity archetype storage unit holding entities and their component columns.
Definition chunk.h:36
GAIA_NODISCARD const uint8_t * comp_ptr(uint32_t compIdx, uint32_t offset) const
Const pointer to a component element within a column.
Definition chunk.h:848
GAIA_NODISCARD decltype(auto) sview_auto(uint16_t from, uint16_t to)
Returns either a mutable or immutable entity/component view based on the requested type....
Definition chunk.h:773
GAIA_NODISCARD uint16_t capacity() const
Returns the number of entities in the chunk.
Definition chunk.h:1762
static void free(Chunk *pChunk)
Releases all memory allocated by pChunk.
Definition chunk.h:463
GAIA_NODISCARD decltype(auto) view() const
Returns a read-only entity or component view.
Definition chunk.h:600
decltype(auto) set_idx(uint16_t row, uint32_t compIdx)
Sets the value of a component using a pre-resolved component column.
Definition chunk.h:1497
void call_ctors(uint32_t entIdx, uint32_t entCnt)
Invokes registered constructors for component columns at a row range.
Definition chunk.h:1398
decltype(auto) sset(uint16_t row, Entity type)
Sets the value of component type at the position row in the chunk.
Definition chunk.h:1556
void save(ser::serializer &s) const
Serializes chunk contents: entity counts, lifespan state, entity ids and component data.
Definition chunk.h:483
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
static void copy_entity_data_n_same_chunk(Chunk *pSrcChunk, uint32_t srcRow, Chunk *pDstChunk, uint32_t dstRow, uint32_t dstCount)
Copies all data associated with srcRow into dstCount consecutive rows in the same-archetype chunk.
Definition chunk.h:903
GAIA_FORCEINLINE void update_world_version()
Update the version of all components.
Definition chunk.h:1818
GAIA_FORCEINLINE void modify()
Marks the component T as modified. Best used with sview to manually trigger an update at user's whim....
Definition chunk.h:698
GAIA_NODISCARD uint32_t comp_idx(Entity entity, uint32_t offset) const
Returns the internal index of a component based on the provided entity.
Definition chunk.h:1632
GAIA_NODISCARD Entity comp_entity() const
Component entity for the chunk archetype contents.
Definition chunk.h:1609
bool progress_death()
Updates internal lifespan.
Definition chunk.h:1716
decltype(auto) set(uint16_t row)
Sets the value of component T on row in the chunk.
Definition chunk.h:1480
GAIA_NODISCARD World & world()
Owning world mutable reference.
Definition chunk.h:797
GAIA_NODISCARD GAIA_FORCEINLINE auto comp_ptr_mut_gen(uint32_t compIdx, uint32_t row)
Returns a read-write span of the component data. Also updates the world version for the component.
Definition chunk.h:304
void die()
Marks the chunk as dead (ready to delete)
Definition chunk.h:1690
GAIA_NODISCARD EntitySpan ids_view() const
Span over the component and entity identifiers held by this chunk.
Definition chunk.h:809
GAIA_NODISCARD uint32_t idx() const
Returns the index of this chunk in its archetype's storage.
Definition chunk.h:1645
void call_all_dtors()
Invokes registered destructors for all custom component instances before release.
Definition chunk.h:1420
GAIA_NODISCARD decltype(auto) sview_auto()
Returns an automatically-typed mutable view without query-version updates.
Definition chunk.h:785
decltype(auto) sset_idx(uint16_t row, uint32_t compIdx)
Sets the value of a component using a pre-resolved component column.
Definition chunk.h:1542
void update_versions()
Updates the version numbers for this chunk.
Definition chunk.h:573
GAIA_NODISCARD decltype(auto) sview_mut_raw(void *ptr, uint32_t size) const
Returns a mutable view over raw bytes without query-version updates.
Definition chunk.h:679
GAIA_NODISCARD decltype(auto) sview_mut()
Returns a mutable entity or component view without query-version updates.
Definition chunk.h:690
static constexpr uint16_t chunk_header_size()
Size in bytes reserved in front of entity and component data, counted from the start of the chunk all...
Definition chunk.h:394
GAIA_FORCEINLINE void update_world_version(uint32_t compIdx)
Update the version of a component at the index.
Definition chunk.h:1798
GAIA_NODISCARD bool has_disabled_entities() const
Checks is this chunk has any disabled entities.
Definition chunk.h:1657
GAIA_NODISCARD bool full() const
Checks is the full capacity of the has has been reached.
Definition chunk.h:1724
GAIA_NODISCARD bool is_semi() const
Checks is the chunk is semi-full.
Definition chunk.h:1730
GAIA_NODISCARD const World & world() const
Owning world const reference.
Definition chunk.h:803
GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const
Returns a read-only entity or component view.
Definition chunk.h:586
GAIA_NODISCARD uint16_t add_entity(Entity entity)
Make.
Definition chunk.h:855
GAIA_NODISCARD uint16_t size_enabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1750
GAIA_NODISCARD decltype(auto) view_auto(uint16_t from, uint16_t to)
Returns either a mutable or immutable entity/component view based on the requested type....
Definition chunk.h:748
static constexpr uint16_t chunk_total_bytes(uint16_t dataSize)
Total chunk allocation size for a given usable data size.
Definition chunk.h:412
void start_dying()
Starts the process of dying (not yet ready to delete, can be revived)
Definition chunk.h:1701
GAIA_NODISCARD bool has() const
Checks if component T is present in the chunk.
Definition chunk.h:1459
GAIA_NODISCARD uint16_t size() const
Returns the total number of entities in the chunk (both enabled and disabled)
Definition chunk.h:1738
GAIA_FORCEINLINE void update_entity_order_version()
Updates the entity-order version after rows were added, removed, or reordered.
Definition chunk.h:1811
GAIA_NODISCARD decltype(auto) view_auto()
Returns an automatically-typed mutable view over the chunk.
Definition chunk.h:760
GAIA_NODISCARD decltype(auto) get(uint16_t row) const
Returns the value stored in component T on row in the chunk.
Definition chunk.h:1577
GAIA_NODISCARD uint32_t delete_queue_index() const
Returns the index inside World's deferred chunk-delete queue.
Definition chunk.h:1675
void load(ser::serializer &s)
Deserializes chunk contents, restoring entity ids and component data.
Definition chunk.h:518
void move_entity_data(Entity entity, uint16_t row, EntityContainers &recs)
Moves all data associated with entity into the chunk so that it is stored at the row row.
Definition chunk.h:995
void set_idx(uint32_t value)
Sets the index of this chunk in its archetype's storage.
Definition chunk.h:1639
GAIA_NODISCARD std::span< const ComponentRecord > comp_rec_view() const
Span over the component records describing each chunk column.
Definition chunk.h:815
void clear_delete_queue_index()
Clears the deferred chunk-delete queue index.
Definition chunk.h:1685
GAIA_NODISCARD bool entity_order_changed(uint32_t requiredVersion) const
Returns true if entity order changed since requiredVersion. This is narrower than changed(requiredVer...
Definition chunk.h:1793
void call_ctor(uint32_t entIdx, uint32_t compIdx, const ComponentCacheItem &item)
Invokes the registered constructor for one component instance.
Definition chunk.h:1385
static constexpr uint16_t chunk_data_bytes(uint16_t totalSize)
Usable data area size for a given total chunk allocation size.
Definition chunk.h:419
GAIA_NODISCARD bool changed(uint32_t requiredVersion, uint32_t compIdx) const
Returns true if the provided version is newer than the one stored internally.
Definition chunk.h:1782
void delete_queue_index(uint32_t idx)
Stores the index inside World's deferred chunk-delete queue.
Definition chunk.h:1680
static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers &recs)
Copies all data associated with srcEntity into dstEntity.
Definition chunk.h:871
bool enabled(uint16_t row) const
Checks if the entity is enabled.
Definition chunk.h:1371
GAIA_NODISCARD decltype(auto) view_mut_raw(void *ptr, uint32_t size) const
Returns a mutable view over raw bytes as typed data.
Definition chunk.h:647
GAIA_NODISCARD bool dying() const
Checks is this chunk is dying.
Definition chunk.h:1663
GAIA_NODISCARD uint8_t * comp_ptr_mut(uint32_t compIdx)
Mutable pointer to the start of a component column.
Definition chunk.h:822
GAIA_NODISCARD bool changed(uint32_t requiredVersion) const
Returns true if the provided version is newer than the one stored internally. Use when checking if th...
Definition chunk.h:1772
void remove_last_entity()
Remove the last entity from a chunk. If as a result the chunk becomes empty it is scheduled for delet...
Definition chunk.h:560
GAIA_NODISCARD uint16_t size_disabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1756
void swap_chunk_entities(uint16_t rowA, uint16_t rowB, EntityContainers &recs)
Tries to swap the entity at row rowA with the one at the row rowB. When swapping, all data associated...
Definition chunk.h:1243
static void move_foreign_entity_data(Chunk *pSrcChunk, uint32_t srcRow, Chunk *pDstChunk, uint32_t dstRow)
Moves all data associated with entity into the chunk so that it is stored at the row row.
Definition chunk.h:1083
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
GAIA_NODISCARD uint32_t comp_idx(Entity entity) const
Returns the internal index of a component based on the provided entity.
Definition chunk.h:1623
static void swap_chunk_entities(World &world, Entity entityA, Entity entityB)
Tries to swap entityA with entityB. When swapping, all data associated with the two entities is swapp...
Definition chunk.h:1287
GAIA_FORCEINLINE void update_world_version_init()
Update the version of all components on chunk init.
Definition chunk.h:1828
GAIA_NODISCARD bool dead() const
Checks is this chunk is dead (ready to delete)
Definition chunk.h:1696
GAIA_NODISCARD uint8_t * comp_ptr_mut(uint32_t compIdx, uint32_t offset)
Mutable pointer to a component element within a column.
Definition chunk.h:831
decltype(auto) set(uint16_t row, Entity type)
Sets the value of component type at the position row in the chunk.
Definition chunk.h:1514
GAIA_NODISCARD bool has_enabled_entities() const
Checks is this chunk has any enabled entities.
Definition chunk.h:1651
GAIA_NODISCARD const uint8_t * comp_ptr(uint32_t compIdx) const
Const pointer to the start of a component column.
Definition chunk.h:839
void revive()
Makes a dying chunk alive again.
Definition chunk.h:1708
GAIA_NODISCARD bool has(Entity entity) const
Checks if a component/entity entity is present in the chunk.
Definition chunk.h:1450
GAIA_NODISCARD bool queued_for_deletion() const
Returns true when the chunk is currently queued for deferred deletion.
Definition chunk.h:1669
GAIA_NODISCARD bool empty() const
Checks is there are any entities in the chunk.
Definition chunk.h:1744
static void copy_foreign_entity_data(Chunk *pSrcChunk, uint32_t srcRow, Chunk *pDstChunk, uint32_t dstRow)
Copies all data associated with entity into the chunk so that it is stored at the row row.
Definition chunk.h:1019
void remove_entity_inter(uint16_t row, EntityContainers &recs)
Tries to remove the entity at row. Removal is done via swapping with last entity in chunk....
Definition chunk.h:1148
GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to)
Returns a mutable entity or component view.
Definition chunk.h:622
void finish_write(uint32_t compIdx, uint16_t from, uint16_t to)
Finishes a raw write over a chunk range by updating versions, running set hooks once,...
Definition chunk.h:321
GAIA_NODISCARD decltype(auto) sview_mut(uint16_t from, uint16_t to)
Returns a mutable component view. Doesn't update the world version when the access is acquired.
Definition chunk.h:662
void diag() const
Logs a diagnostic line describing the chunk capacity and lifespan state.
Definition chunk.h:1837
GAIA_NODISCARD decltype(auto) view_raw(const void *ptr, uint32_t size) const
Returns a read-only view over raw bytes as typed data.
Definition chunk.h:610
GAIA_NODISCARD decltype(auto) get_idx(uint16_t row, uint32_t compIdx) const
Returns the value stored in component T using a pre-resolved component column.
Definition chunk.h:1587
GAIA_NODISCARD decltype(auto) get(uint16_t row, Entity type) const
Returns the value stored in component type on row in the chunk.
Definition chunk.h:1598
GAIA_NODISCARD EntitySpan entity_view() const
Span over the entities stored in this chunk.
Definition chunk.h:791
GAIA_NODISCARD decltype(auto) view_mut()
Returns a mutable entity or component view.
Definition chunk.h:637
static Chunk * create(const World &wld, const ComponentCache &cc, uint32_t chunkIndex, uint16_t capacity, uint8_t cntEntities, uint16_t dataBytes, uint32_t &worldVersion, const ChunkDataOffsets &offsets, const Entity *ids, const ComponentCacheItem *const *pItems, const ChunkDataOffset *compOffs)
Allocates memory for a new chunk.
Definition chunk.h:425
decltype(auto) sset(uint16_t row)
Sets the value of component T on row in the chunk.
Definition chunk.h:1533
static void copy_foreign_entity_data_n(Chunk *pSrcChunk, uint32_t srcRow, Chunk *pDstChunk, uint32_t dstRow, uint32_t dstCount)
Copies all data associated with srcRow into dstCount consecutive rows in a foreign chunk.
Definition chunk.h:935
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
Strict weak ordering functor using operator<.
Definition utility.h:1385
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:231
Detects whether a type is a relationship pair.
Definition id.h:220
Read-only view for a selected layout and item type.
Definition data_layout_policy.h:127
Mutable view for a selected layout and item type.
Definition data_layout_policy.h:132
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