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
61 uint8_t m_data[1];
62
63 GAIA_MSVC_WARNING_PUSH()
64 GAIA_MSVC_WARNING_DISABLE(26495)
65
66 // Hidden default constructor. Only use to calculate the relative offset of m_data
67 Chunk() = default;
68
69 Chunk(
70 const World& wld, const ComponentCache& cc, //
71 uint32_t chunkIndex, uint16_t capacity, uint8_t genEntities, //
72 uint32_t& worldVersion): //
73 m_header(wld, cc, chunkIndex, capacity, genEntities, worldVersion) {
74 // Chunk data area consist of memory offsets, entities, and component data. Normally, we would need
75 // to in-place construct all of it manually.
76 // However, the memory offsets and entities are all trivial types and components are initialized via
77 // their constructors on-demand (if not trivial) so we do not really need to do any construction here.
78 }
79
80 GAIA_MSVC_WARNING_POP()
81
82 GAIA_CLANG_WARNING_PUSH()
83 // Memory is aligned so we can silence this warning
84 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
85
86 void init(
87 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*)&data(headerOffsets.firstByte_Versions);
93
94 // Cache entity ids
95 if (cntEntities > 0) {
96 auto* dst = m_records.pCompEntities = (Entity*)&data(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*)&data(headerOffsets.firstByte_Records);
110 GAIA_FOR_(cntEntities, j) {
111 dst[j].comp =
112 pItems[j] == nullptr ? Component(IdentifierIdBad, 0, 0, 0, DataStorageType::Table) : pItems[j]->comp;
113 dst[j].pData = &data(compOffs[j]);
114 dst[j].pItem = pItems[j];
115 }
116 }
117
118 m_records.pEntities = (Entity*)&data(headerOffsets.firstByte_EntityData);
119
120 // Now that records are set, we use the cached component descriptors to set ctor/dtor masks.
121 {
122 auto recs = comp_rec_view();
123 const auto recs_cnt = recs.size();
124 GAIA_FOR(recs_cnt) {
125 const auto& rec = recs[i];
126 if (!component_uses_table_storage(rec.comp))
127 continue;
128
129 const auto e = m_records.pCompEntities[i];
130 if (e.kind() == EntityKind::EK_Gen) {
131 m_header.hasAnyCustomGenCtor |= (rec.pItem->func_ctor != nullptr);
132 m_header.hasAnyCustomGenDtor |= (rec.pItem->func_dtor != nullptr);
133 } else {
134 m_header.hasAnyCustomUniCtor |= (rec.pItem->func_ctor != nullptr);
135 m_header.hasAnyCustomUniDtor |= (rec.pItem->func_dtor != nullptr);
136
137 // We construct unique components right away if possible
138 call_ctor(0, i, *rec.pItem);
139 }
140 }
141 }
142
143 // Make sure world versions are set initially.
144 update_world_version_init();
145 }
146
147 GAIA_CLANG_WARNING_POP()
148
149
151 GAIA_NODISCARD std::span<const ComponentVersion> comp_version_view() const {
152 return {(const ComponentVersion*)m_records.pVersions, (size_t)m_header.cntEntities + 1};
153 }
154
157 GAIA_NODISCARD std::span<ComponentVersion> comp_version_view_mut() {
158 return {m_records.pVersions, (size_t)m_header.cntEntities + 1};
159 }
160
161 GAIA_NODISCARD std::span<Entity> entity_view_mut() {
162 return {m_records.pEntities, m_header.count};
163 }
164
172 template <typename T>
173 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) const //
174 -> decltype(std::span<const uint8_t>{}) {
175
176 if constexpr (std::is_same_v<core::raw_t<T>, Entity>) {
177 GAIA_ASSERT(to <= m_header.count);
178 return {(const uint8_t*)&m_records.pEntities[from], to - from};
179 } else if constexpr (is_pair<T>::value) {
180 using TT = typename T::type;
181 using U = typename component_type_t<TT>::Type;
182 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
183
184 constexpr auto kind = entity_kind_v<TT>;
185
186 if constexpr (mem::is_soa_layout_v<U>) {
187 GAIA_ASSERT(from == 0);
188 GAIA_ASSERT(to == capacity());
189 return {comp_ptr(compIdx), to};
190 } else if constexpr (kind == EntityKind::EK_Gen) {
191 GAIA_ASSERT(to <= m_header.count);
192 return {comp_ptr(compIdx, from), to - from};
193 } else {
194 GAIA_ASSERT(to <= m_header.count);
195 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
196 return {comp_ptr(compIdx), 1};
197 }
198 } else {
199 using U = typename component_type_t<T>::Type;
200 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
201
202 constexpr auto kind = entity_kind_v<T>;
203
204 if constexpr (mem::is_soa_layout_v<U>) {
205 GAIA_ASSERT(from == 0);
206 GAIA_ASSERT(to == capacity());
207 return {comp_ptr(compIdx), to};
208 } else if constexpr (kind == EntityKind::EK_Gen) {
209 GAIA_ASSERT(to <= m_header.count);
210 return {comp_ptr(compIdx, from), to - from};
211 } else {
212 GAIA_ASSERT(to <= m_header.count);
213 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
214 return {comp_ptr(compIdx), 1};
215 }
216 }
217 }
218
225 template <typename T>
226 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter(uint32_t from, uint32_t to) const //
227 -> decltype(std::span<const uint8_t>{}) {
228 if constexpr (std::is_same_v<core::raw_t<T>, Entity>)
229 return view_inter_idx<T>(BadIndex, from, to);
230 else if constexpr (is_pair<T>::value) {
231 const auto rel = m_header.cc->get<typename T::rel>().entity;
232 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
233 return view_inter_idx<T>(comp_idx((Entity)Pair(rel, tgt)), from, to);
234 } else {
235 const auto comp = m_header.cc->get<T>().entity;
236#if GAIA_ASSERT_ENABLED
237 constexpr auto kind = entity_kind_v<T>;
238 GAIA_ASSERT(comp.kind() == kind);
239#endif
240 return view_inter_idx<T>(comp_idx(comp), from, to);
241 }
242 }
243
252 template <typename T, bool WorldVersionUpdateWanted>
253 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) //
254 -> decltype(std::span<uint8_t>{}) {
255 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "view_mut can't be used to modify Entity");
256
257 if constexpr (is_pair<T>::value) {
258 using TT = typename T::type;
259 using U = typename component_type_t<TT>::Type;
260 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
261
262 constexpr auto kind = entity_kind_v<TT>;
263
264 // Update version number if necessary so we know RW access was used on the chunk
265 if constexpr (WorldVersionUpdateWanted) {
266 update_world_version(compIdx);
267
268#if GAIA_ENABLE_SET_HOOKS
269 const auto& rec = m_records.pRecords[compIdx];
270 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
271 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
272#endif
273 }
274
275 if constexpr (mem::is_soa_layout_v<U>) {
276 GAIA_ASSERT(from == 0);
277 GAIA_ASSERT(to == capacity());
278 return {comp_ptr_mut(compIdx), to};
279 } else if constexpr (kind == EntityKind::EK_Gen) {
280 GAIA_ASSERT(to <= m_header.count);
281 return {comp_ptr_mut(compIdx, from), to - from};
282 } else {
283 GAIA_ASSERT(to <= m_header.count);
284 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
285 return {comp_ptr_mut(compIdx), 1};
286 }
287 } else {
288 using U = typename component_type_t<T>::Type;
289 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
290 constexpr auto kind = entity_kind_v<T>;
291
292 // Update version number if necessary so we know RW access was used on the chunk
293 if constexpr (WorldVersionUpdateWanted) {
294 update_world_version(compIdx);
295
296#if GAIA_ENABLE_SET_HOOKS
297 const auto& rec = m_records.pRecords[compIdx];
298 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
299 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
300#endif
301 }
302
303 if constexpr (mem::is_soa_layout_v<U>) {
304 GAIA_ASSERT(from == 0);
305 GAIA_ASSERT(to == capacity());
306 return {comp_ptr_mut(compIdx), to};
307 } else if constexpr (kind == EntityKind::EK_Gen) {
308 GAIA_ASSERT(to <= m_header.count);
309 return {comp_ptr_mut(compIdx, from), to - from};
310 } else {
311 GAIA_ASSERT(to <= m_header.count);
312 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
313 return {comp_ptr_mut(compIdx), 1};
314 }
315 }
316 }
317
318 template <typename T, bool WorldVersionUpdateWanted>
319 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter(uint32_t from, uint32_t to) //
320 -> decltype(std::span<uint8_t>{}) {
321 if constexpr (is_pair<T>::value) {
322 const auto rel = m_header.cc->get<typename T::rel>().entity;
323 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
324 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx((Entity)Pair(rel, tgt)), from, to);
325 } else {
326 const auto comp = m_header.cc->get<T>().entity;
327#if GAIA_ASSERT_ENABLED
328 constexpr auto kind = entity_kind_v<T>;
329 GAIA_ASSERT(comp.kind() == kind);
330#endif
331 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx(comp), from, to);
332 }
333 }
334
335 public:
342 template <bool WorldVersionUpdateWanted>
343 GAIA_NODISCARD GAIA_FORCEINLINE auto comp_ptr_mut_gen(uint32_t compIdx, uint32_t row) {
344 // Update version number if necessary so we know RW access was used on the chunk
345 if constexpr (WorldVersionUpdateWanted) {
346 update_world_version(compIdx);
347
348#if GAIA_ENABLE_SET_HOOKS
349 const auto& rec = m_records.pRecords[compIdx];
350 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
351 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
352#endif
353 }
354
355 return comp_ptr_mut(compIdx, row);
356 }
357
360 void finish_write(uint32_t compIdx, uint16_t from, uint16_t to) {
361 GAIA_ASSERT(compIdx < m_header.cntEntities);
362 if (from >= to)
363 return;
364
365 update_world_version(compIdx);
366
367#if GAIA_ENABLE_SET_HOOKS
368 const auto& rec = m_records.pRecords[compIdx];
369 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
370 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
371#endif
372
373 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, from, to);
374 }
375
376 private:
383 template <typename T>
384 GAIA_NODISCARD decltype(auto) comp_inter(uint16_t row) const {
385 using U = typename actual_type_t<T>::Type;
386 using RetValueType = decltype(view<T>()[0]);
387
388 GAIA_ASSERT(row < m_header.count);
389 if constexpr (mem::is_soa_layout_v<U>)
390 return view<T>(0, capacity())[row];
391 else if constexpr (sizeof(RetValueType) <= 8)
392 return view<T>()[row];
393 else
394 return (const U&)view<T>()[row];
395 }
396
397 template <typename T>
398 GAIA_NODISCARD decltype(auto) comp_inter_idx(uint16_t row, uint32_t compIdx) const {
399 using U = typename actual_type_t<T>::Type;
400 using RetValueType = decltype(view_raw<T>((const void*)nullptr, 1)[0]);
401
402 GAIA_ASSERT(row < m_header.count);
403 if constexpr (mem::is_soa_layout_v<U>)
404 return view_raw<T>(comp_ptr(compIdx), capacity())[row];
405 else if constexpr (actual_type_t<T>::Kind == EntityKind::EK_Gen) {
406 if constexpr (sizeof(RetValueType) <= 8)
407 return view_raw<T>(comp_ptr(compIdx, row), 1)[0];
408 else
409 return (const U&)view_raw<T>(comp_ptr(compIdx, row), 1)[0];
410 } else {
411 if constexpr (sizeof(RetValueType) <= 8)
412 return view_raw<T>(comp_ptr(compIdx), 1)[0];
413 else
414 return (const U&)view_raw<T>(comp_ptr(compIdx), 1)[0];
415 }
416 }
417
418 template <typename T, bool WorldVersionUpdateWanted>
419 GAIA_NODISCARD decltype(auto) comp_mut_idx(uint16_t row, uint32_t compIdx) {
420 using U = typename actual_type_t<T>::Type;
421
422 GAIA_ASSERT(row < m_header.capacity);
423 if constexpr (mem::is_soa_layout_v<U>)
424 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), capacity())[row];
425 else if constexpr (actual_type_t<T>::Kind == EntityKind::EK_Gen)
426 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, row), 1)[0];
427 else
428 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), 1)[0];
429 }
430
431 public:
432 Chunk(const Chunk& chunk) = delete;
433 Chunk(Chunk&& chunk) = delete;
434 Chunk& operator=(const Chunk& chunk) = delete;
435 Chunk& operator=(Chunk&& chunk) = delete;
436 ~Chunk() = default;
437
440 static constexpr uint16_t chunk_header_size() {
441 const auto dataAreaOffset =
442 // ChunkAllocator reserves the first few bytes for internal purposes
443 MemoryBlockUsableOffset +
444 // Chunk "header" area (before actual entity/component data starts)
445 sizeof(ChunkHeader) + sizeof(ChunkRecords);
446 static_assert(dataAreaOffset % MemoryBlockAlignment == 0);
447 static_assert(dataAreaOffset < UINT16_MAX);
448 return dataAreaOffset;
449 }
450
454 static constexpr uint16_t chunk_total_bytes(uint16_t dataSize) {
455 return chunk_header_size() + dataSize;
456 }
457
461 static constexpr uint16_t chunk_data_bytes(uint16_t totalSize) {
462 return totalSize - chunk_header_size();
463 }
464
467 static uintptr_t chunk_data_area_offset() {
468 // Note, offsetof is implementation-defined and conditionally-supported since C++17.
469 // Therefore, we instantiate the chunk and calculate the relative address ourselves.
470 Chunk chunk;
471 const auto chunk_offset = (uintptr_t)&chunk;
472 const auto data_offset = (uintptr_t)&chunk.m_data[0];
473 return data_offset - chunk_offset;
474 }
475
478 static Chunk* create(
479 const World& wld, const ComponentCache& cc, //
480 uint32_t chunkIndex, uint16_t capacity, uint8_t cntEntities, uint8_t genEntities, //
481 uint16_t dataBytes, uint32_t& worldVersion,
482 // data offsets
483 const ChunkDataOffsets& offsets,
484 // component entities
485 const Entity* ids,
486 // resolved component storage items
487 const ComponentCacheItem* const* pItems,
488 // component offsets
489 const ChunkDataOffset* compOffs) {
490 const auto totalBytes = chunk_total_bytes(dataBytes);
491#if GAIA_ECS_CHUNK_ALLOCATOR
492 auto* pChunk = (Chunk*)ChunkAllocator::get().alloc(totalBytes);
493 (void)new (pChunk) Chunk(wld, cc, chunkIndex, capacity, genEntities, worldVersion);
494#else
495 GAIA_ASSERT(totalBytes <= MaxMemoryBlockSize);
496 const auto sizeType = mem_block_size_type(totalBytes);
497 const auto allocSize = mem_block_size(sizeType);
498 auto* pChunkMem = mem::AllocHelper::alloc<uint8_t>(allocSize);
499 std::memset(pChunkMem, 0, allocSize);
500 auto* pChunk = new (pChunkMem) Chunk(wld, cc, chunkIndex, capacity, genEntities, worldVersion);
501#endif
502
503 pChunk->init((uint32_t)cntEntities, ids, pItems, offsets, compOffs);
504 return pChunk;
505 }
506
509 static void free(Chunk* pChunk) {
510 GAIA_ASSERT(pChunk != nullptr);
511 GAIA_ASSERT(!pChunk->dead());
512
513 // Mark as dead
514 pChunk->die();
515
516 // Call destructors for components that need it
517 pChunk->call_all_dtors();
518
519 pChunk->~Chunk();
520#if GAIA_ECS_CHUNK_ALLOCATOR
521 ChunkAllocator::get().free(pChunk);
522#else
523 mem::AllocHelper::free((uint8_t*)pChunk);
524#endif
525 }
526
529 void save(ser::serializer& s) const {
530 s.save(m_header.count);
531 if (m_header.count == 0)
532 return;
533
534 s.save(m_header.countEnabled);
535
536 const uint16_t dead = m_header.dead;
537 const uint16_t lifespanCountdown = m_header.lifespanCountdown;
538 s.save(dead);
539 s.save(lifespanCountdown);
540
541 const auto cnt = (uint32_t)m_header.count;
542 const auto cap = (uint32_t)m_header.capacity;
543
544 // Store entity data
545 {
546 const auto* pData = m_records.pEntities;
547 GAIA_FOR(cnt) s.save(pData[i]);
548 }
549
550 // Store component data
551 {
552 for (const auto& rec: comp_rec_view()) {
553 // Skip the component if there's no size associated with it
554 if (!component_uses_table_storage(rec.comp))
555 continue;
556
557 rec.pItem->save(s, rec.pData, 0, cnt, cap);
558 }
559 }
560 }
561
565 uint16_t prevCount = m_header.count;
566 s.load(m_header.count);
567 if (m_header.count == 0)
568 return;
569
570 s.load(m_header.countEnabled);
571
572 uint16_t dead = 0;
573 uint16_t lifespanCountdown = 0;
574 s.load(dead);
575 s.load(lifespanCountdown);
576 m_header.dead = dead != 0;
577 m_header.lifespanCountdown = lifespanCountdown;
578
579 const auto cnt = (uint32_t)m_header.count;
580 const auto cap = (uint32_t)m_header.capacity;
581
582 // Load entity data
583 {
584 GAIA_FOR(cnt) {
585 Entity e;
586 s.load(e);
587 entity_view_mut()[i] = e;
588 }
589 }
590
591 // Load component data. Call constructors first as necessary.
592 call_gen_ctors(prevCount, cnt);
593 {
594 for (const auto& rec: comp_rec_view()) {
595 // Skip the component if there's no size associated with it
596 if (!component_uses_table_storage(rec.comp))
597 continue;
598
599 rec.pItem->load(s, rec.pData, 0, cnt, cap);
600 }
601 }
602 }
603
607 // Should never be called over an empty chunk
608 GAIA_ASSERT(!empty());
609
610#if GAIA_ASSERT_ENABLED
611 // Invalidate the entity in chunk data
612 entity_view_mut()[m_header.count - 1] = EntityBad;
613#endif
614
615 --m_header.count;
616 }
617
620 ::gaia::ecs::update_version(m_header.worldVersion);
621 update_world_version();
622 update_entity_order_version();
623 }
624
631 template <typename T>
632 GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const {
633 using U = typename actual_type_t<T>::Type;
634
635 // Always consider full range for SoA
636 if constexpr (mem::is_soa_layout_v<U>)
637 return mem::auto_view_policy_get<U>{view_inter<T>(0, capacity())};
638 else
639 return mem::auto_view_policy_get<U>{view_inter<T>(from, to)};
640 }
641
645 template <typename T>
646 GAIA_NODISCARD decltype(auto) view() const {
647 return view<T>(0, m_header.count);
648 }
649
655 template <typename T>
656 GAIA_NODISCARD decltype(auto) view_raw(const void* ptr, uint32_t size) const {
657 using U = typename actual_type_t<T>::Type;
658 return mem::auto_view_policy_get<U>{std::span{(const uint8_t*)ptr, size}};
659 }
660
667 template <typename T>
668 GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to) {
669 using U = typename actual_type_t<T>::Type;
670 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
671
672 // Always consider full range for SoA
673 if constexpr (mem::is_soa_layout_v<U>)
674 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(0, capacity())};
675 else
676 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(from, to)};
677 }
678
682 template <typename T>
683 GAIA_NODISCARD decltype(auto) view_mut() {
684 return view_mut<T>(0, m_header.count);
685 }
686
692 template <typename T>
693 GAIA_NODISCARD decltype(auto) view_mut_raw(void* ptr, uint32_t size) const {
694 using U = typename actual_type_t<T>::Type;
695 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
696
697 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
698 }
699
707 template <typename T>
708 GAIA_NODISCARD decltype(auto) sview_mut(uint16_t from, uint16_t to) {
709 using U = typename actual_type_t<T>::Type;
710 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
711
712 // Always consider full range for SoA
713 if constexpr (mem::is_soa_layout_v<U>)
714 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(0, capacity())};
715 else
716 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(from, to)};
717 }
718
724 template <typename T>
725 GAIA_NODISCARD decltype(auto) sview_mut_raw(void* ptr, uint32_t size) const {
726 using U = typename actual_type_t<T>::Type;
727 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
728
729 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
730 }
731
735 template <typename T>
736 GAIA_NODISCARD decltype(auto) sview_mut() {
737 return sview_mut<T>(0, m_header.count);
738 }
739
743 template <
744 typename T
745#if GAIA_ENABLE_HOOKS
746 ,
747 bool TriggerSetHooks
748#endif
749 >
750 GAIA_FORCEINLINE void modify() {
751 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "mod can't be used to modify Entity");
752
753 if constexpr (is_pair<T>::value) {
754 using TT = typename T::type;
755 using U = typename component_type_t<TT>::Type;
756 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
757
758#if GAIA_ASSERT_ENABLED
759 // constexpr auto kind = entity_kind_v<TT>;
760#endif
761 const auto rel = m_header.cc->get<typename T::rel>().entity;
762 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
763 const auto compIdx = comp_idx((Entity)Pair(rel, tgt));
764
765 // Update version number if necessary so we know RW access was used on the chunk
766 update_world_version(compIdx);
767
768#if GAIA_ENABLE_SET_HOOKS
769 if constexpr (TriggerSetHooks) {
770 const auto& rec = m_records.pRecords[compIdx];
771 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
772 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
773 }
774#endif
775 } else {
776 using U = typename component_type_t<T>::Type;
777 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
778
779#if GAIA_ASSERT_ENABLED
780 constexpr auto kind = entity_kind_v<T>;
781#endif
782 const auto comp = m_header.cc->get<T>().entity;
783 GAIA_ASSERT(comp.kind() == kind);
784 const auto compIdx = comp_idx(comp);
785
786 // Update version number if necessary so we know RW access was used on the chunk
787 update_world_version(compIdx);
788
789#if GAIA_ENABLE_SET_HOOKS
790 if constexpr (TriggerSetHooks) {
791 const auto& rec = m_records.pRecords[compIdx];
792 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
793 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
794 }
795#endif
796 }
797 }
798
806 template <typename T>
807 GAIA_NODISCARD decltype(auto) view_auto(uint16_t from, uint16_t to) {
808 using UOriginal = typename actual_type_t<T>::TypeOriginal;
809 if constexpr (core::is_mut_v<UOriginal>)
810 return view_mut<T>(from, to);
811 else
812 return view<T>(from, to);
813 }
814
818 template <typename T>
819 GAIA_NODISCARD decltype(auto) view_auto() {
820 return view_auto<T>(0, m_header.count);
821 }
822
831 template <typename T>
832 GAIA_NODISCARD decltype(auto) sview_auto(uint16_t from, uint16_t to) {
833 using UOriginal = typename actual_type_t<T>::TypeOriginal;
834 if constexpr (core::is_mut_v<UOriginal>)
835 return sview_mut<T>(from, to);
836 else
837 return view<T>(from, to);
838 }
839
843 template <typename T>
844 GAIA_NODISCARD decltype(auto) sview_auto() {
845 return sview_auto<T>(0, m_header.count);
846 }
847
850 GAIA_NODISCARD EntitySpan entity_view() const {
851 return {(const Entity*)m_records.pEntities, m_header.count};
852 }
853
856 GAIA_NODISCARD World& world() {
857 return *const_cast<World*>(m_header.world);
858 }
859
862 GAIA_NODISCARD const World& world() const {
863 return *m_header.world;
864 }
865
868 GAIA_NODISCARD EntitySpan ids_view() const {
869 return {(const Entity*)m_records.pCompEntities, m_header.cntEntities};
870 }
871
874 GAIA_NODISCARD std::span<const ComponentRecord> comp_rec_view() const {
875 return {m_records.pRecords, m_header.cntEntities};
876 }
877
881 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx) {
882 const auto& rec = m_records.pRecords[compIdx];
883 return rec.pData;
884 }
885
890 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx, uint32_t offset) {
891 const auto& rec = m_records.pRecords[compIdx];
892 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
893 }
894
898 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx) const {
899 const auto& rec = m_records.pRecords[compIdx];
900 return rec.pData;
901 }
902
907 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx, uint32_t offset) const {
908 const auto& rec = m_records.pRecords[compIdx];
909 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
910 }
911
914 GAIA_NODISCARD uint16_t add_entity(Entity entity) {
915 const auto row = m_header.count++;
916
917 // Zero after increase of value means an overflow!
918 GAIA_ASSERT(m_header.count != 0);
919
920 ++m_header.countEnabled;
921 entity_view_mut()[row] = entity;
922
923 return row;
924 }
925
930 static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers& recs) {
931 GAIA_PROF_SCOPE(Chunk::copy_entity_data);
932
933 auto& srcEntityContainer = recs[srcEntity];
934 auto* pSrcChunk = srcEntityContainer.pChunk;
935
936 auto& dstEntityContainer = recs[dstEntity];
937 auto* pDstChunk = dstEntityContainer.pChunk;
938
939 GAIA_ASSERT(srcEntityContainer.pArchetype == dstEntityContainer.pArchetype);
940
941 auto srcRecs = pSrcChunk->comp_rec_view();
942
943 // Copy generic component data from reference entity to our new entity.
944 // Unique components do not change place in the chunk so there is no need to move them.
945 GAIA_FOR(pSrcChunk->m_header.genEntities) {
946 const auto& rec = srcRecs[i];
947 if (!component_uses_table_storage(rec.comp))
948 continue;
949
950 const auto* pSrc = (const void*)pSrcChunk->comp_ptr_mut(i);
951 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
952 rec.pItem->copy(
953 pDst, pSrc, dstEntityContainer.row, srcEntityContainer.row, pDstChunk->capacity(), pSrcChunk->capacity());
954 }
955 }
956
964 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
965 GAIA_PROF_SCOPE(Chunk::copy_entity_data_n_same_chunk);
966
967 GAIA_ASSERT(pSrcChunk != nullptr);
968 GAIA_ASSERT(pDstChunk != nullptr);
969 GAIA_ASSERT(srcRow < pSrcChunk->size());
970 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
971 GAIA_ASSERT(pSrcChunk->ids_view().size() == pDstChunk->ids_view().size());
972
973 auto srcRecs = pSrcChunk->comp_rec_view();
974
975 // Copy generic component data from the reference entity to all newly allocated rows.
976 // Unique components do not change place in the chunk so there is no need to move them.
977 GAIA_FOR(pSrcChunk->m_header.genEntities) {
978 const auto& rec = srcRecs[i];
979 if (!component_uses_table_storage(rec.comp))
980 continue;
981
982 const auto* pSrc = (const void*)pSrcChunk->comp_ptr(i);
983 GAIA_FOR_(dstCount, rowOffset) {
984 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
985 rec.pItem->copy(pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
986 }
987 }
988 }
989
997 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
998 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data_n);
999
1000 GAIA_ASSERT(pSrcChunk != nullptr);
1001 GAIA_ASSERT(pDstChunk != nullptr);
1002 GAIA_ASSERT(srcRow < pSrcChunk->size());
1003 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
1004
1005 auto srcIds = pSrcChunk->ids_view();
1006 auto dstIds = pDstChunk->ids_view();
1007 auto dstRecs = pDstChunk->comp_rec_view();
1008
1009 uint32_t i = 0;
1010 uint32_t j = 0;
1011 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1012 const auto oldId = srcIds[i];
1013 const auto newId = dstIds[j];
1014
1015 if (oldId == newId) {
1016 const auto& rec = dstRecs[j];
1017 if (component_uses_table_storage(rec.comp)) {
1018 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1019 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1020 GAIA_FOR_(dstCount, rowOffset) {
1021 rec.pItem->ctor_copy(
1022 pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1023 }
1024 }
1025
1026 ++i;
1027 ++j;
1028 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1029 ++i;
1030 } else {
1031 const auto& rec = dstRecs[j];
1032 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1033 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1034 rec.pItem->func_ctor(pDst, dstCount);
1035 }
1036
1037 ++j;
1038 }
1039 }
1040
1041 for (; j < pDstChunk->m_header.genEntities; ++j) {
1042 const auto& rec = dstRecs[j];
1043 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1044 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1045 rec.pItem->func_ctor(pDst, dstCount);
1046 }
1047 }
1048 }
1049
1054 void move_entity_data(Entity entity, uint16_t row, EntityContainers& recs) {
1055 GAIA_PROF_SCOPE(Chunk::move_entity_data);
1056
1057 auto& ec = recs[entity];
1058 auto* pSrcChunk = ec.pChunk;
1059 auto srcRecs = pSrcChunk->comp_rec_view();
1060
1061 // Copy generic component data from reference entity to our new entity.
1062 // Unique components do not change place in the chunk so there is no need to move them.
1063 GAIA_FOR(pSrcChunk->m_header.genEntities) {
1064 const auto& rec = srcRecs[i];
1065 if (!component_uses_table_storage(rec.comp))
1066 continue;
1067
1068 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1069 auto* pDst = (void*)comp_ptr_mut(i);
1070 rec.pItem->ctor_move(pDst, pSrc, row, ec.row, capacity(), pSrcChunk->capacity());
1071 }
1072 }
1073
1079 static void copy_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1080 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data);
1081
1082 GAIA_ASSERT(pSrcChunk != nullptr);
1083 GAIA_ASSERT(pDstChunk != nullptr);
1084 GAIA_ASSERT(srcRow < pSrcChunk->size());
1085 GAIA_ASSERT(dstRow < pDstChunk->size());
1086
1087 auto srcIds = pSrcChunk->ids_view();
1088 auto dstIds = pDstChunk->ids_view();
1089 auto dstRecs = pDstChunk->comp_rec_view();
1090
1091 // Find intersection of the two component lists.
1092 // Arrays are sorted so we can do linear intersection lookup.
1093 // Call constructor on each match.
1094 // Unique components do not change place in the chunk so there is no need to move them.
1095 {
1096 uint32_t i = 0;
1097 uint32_t j = 0;
1098 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1099 const auto oldId = srcIds[i];
1100 const auto newId = dstIds[j];
1101
1102 if (oldId == newId) {
1103 const auto& rec = dstRecs[j];
1104 if (component_uses_table_storage(rec.comp)) {
1105 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1106 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1107 rec.pItem->ctor_copy(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1108 }
1109
1110 ++i;
1111 ++j;
1112 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1113 ++i;
1114 } else {
1115 // No match with the old chunk. Construct the component
1116 const auto& rec = dstRecs[j];
1117 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1118 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1119 rec.pItem->func_ctor(pDst, 1);
1120 }
1121
1122 ++j;
1123 }
1124 }
1125
1126 // Initialize the rest of the components if they are generic.
1127 for (; j < pDstChunk->m_header.genEntities; ++j) {
1128 const auto& rec = dstRecs[j];
1129 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1130 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1131 rec.pItem->func_ctor(pDst, 1);
1132 }
1133 }
1134 }
1135 }
1136
1142 static void move_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1143 GAIA_PROF_SCOPE(Chunk::move_foreign_entity_data);
1144
1145 GAIA_ASSERT(pSrcChunk != nullptr);
1146 GAIA_ASSERT(pDstChunk != nullptr);
1147 GAIA_ASSERT(srcRow < pSrcChunk->size());
1148 GAIA_ASSERT(dstRow < pDstChunk->size());
1149
1150 auto srcIds = pSrcChunk->ids_view();
1151 auto dstIds = pDstChunk->ids_view();
1152 auto dstRecs = pDstChunk->comp_rec_view();
1153
1154 // Find intersection of the two component lists.
1155 // Arrays are sorted so we can do linear intersection lookup.
1156 // Call constructor on each match.
1157 // Unique components do not change place in the chunk so there is no need to move them.
1158 {
1159 uint32_t i = 0;
1160 uint32_t j = 0;
1161 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1162 const auto oldId = srcIds[i];
1163 const auto newId = dstIds[j];
1164
1165 if (oldId == newId) {
1166 const auto& rec = dstRecs[j];
1167 if (component_uses_table_storage(rec.comp)) {
1168 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1169 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1170 rec.pItem->ctor_move(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1171 }
1172
1173 ++i;
1174 ++j;
1175 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1176 ++i;
1177 } else {
1178 // No match with the old chunk. Construct the component
1179 const auto& rec = dstRecs[j];
1180 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1181 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1182 rec.pItem->func_ctor(pDst, 1);
1183 }
1184
1185 ++j;
1186 }
1187 }
1188
1189 // Initialize the rest of the components if they are generic.
1190 for (; j < pDstChunk->m_header.genEntities; ++j) {
1191 const auto& rec = dstRecs[j];
1192 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1193 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1194 rec.pItem->func_ctor(pDst, 1);
1195 }
1196 }
1197 }
1198 }
1199
1206 void remove_entity_inter(uint16_t row, EntityContainers& recs) {
1207 GAIA_PROF_SCOPE(Chunk::remove_entity_inter);
1208
1209 const uint16_t rowA = row;
1210 const uint16_t rowB = m_header.count - 1;
1211 // The "rowA" entity is the one we are going to destroy so it needs to precede the "rowB"
1212 GAIA_ASSERT(rowA <= rowB);
1213
1214 // To move anything, we need at least 2 entities
1215 if GAIA_LIKELY (rowA < rowB) {
1216 GAIA_ASSERT(m_header.count > 1);
1217
1218 auto ev = entity_view_mut();
1219
1220 // Update entity data
1221 const auto entityB = ev[rowB];
1222 auto& ecB = recs[entityB];
1223#if GAIA_ASSERT_ENABLED
1224 const auto entityA = ev[rowA];
1225 auto& ecA = recs[entityA];
1226
1227 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1228 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1229#endif
1230
1231 ev[rowA] = entityB;
1232
1233 // Move component data from entityB to entityA
1234 auto recView = comp_rec_view();
1235 GAIA_FOR(m_header.genEntities) {
1236 const auto& rec = recView[i];
1237 if (!component_uses_table_storage(rec.comp))
1238 continue;
1239
1240 auto* pSrc = (void*)comp_ptr_mut(i);
1241 rec.pItem->move(pSrc, pSrc, rowA, rowB, capacity(), capacity());
1242
1243 pSrc = (void*)comp_ptr_mut(i, rowB);
1244 rec.pItem->dtor(pSrc);
1245 }
1246
1247 // Entity has been replaced with the last one in our chunk. Update its container record.
1248 ecB.row = rowA;
1249 ecB.pEntity = &ev[rowA];
1250 } else if (m_header.hasAnyCustomGenDtor) {
1251 // This is the last entity in the chunk so simply destroy its data
1252 auto recView = comp_rec_view();
1253 GAIA_FOR(m_header.genEntities) {
1254 const auto& rec = recView[i];
1255 if (!component_uses_table_storage(rec.comp))
1256 continue;
1257
1258 auto* pSrc = (void*)comp_ptr_mut(i, rowA);
1259 rec.pItem->dtor(pSrc);
1260 }
1261 }
1262 }
1263
1270 void remove_entity(uint16_t row, EntityContainers& recs) {
1271 if GAIA_UNLIKELY (m_header.count == 0)
1272 return;
1273
1274 GAIA_PROF_SCOPE(Chunk::remove_entity);
1275
1276 if (enabled(row)) {
1277 // Entity was previously enabled. Swap with the last entity
1278 remove_entity_inter(row, recs);
1279 // If this was the first enabled entity make sure to update the row
1280 if (m_header.rowFirstEnabledEntity > 0 && row == m_header.rowFirstEnabledEntity)
1281 --m_header.rowFirstEnabledEntity;
1282 // At this point the last entity is no longer valid so remove it
1283 remove_last_entity();
1284 --m_header.countEnabled;
1285 } else {
1286 // Entity was previously disabled. Swap with the last disabled entity
1287 const uint16_t pivot = size_disabled() - 1;
1288 swap_chunk_entities(row, pivot, recs);
1289 // Once swapped, try to swap with the last (enabled) entity in the chunk.
1290 remove_entity_inter(pivot, recs);
1291 --m_header.rowFirstEnabledEntity;
1292 // At this point the last entity is no longer valid so remove it
1293 remove_last_entity();
1294 }
1295 }
1296
1304 void swap_chunk_entities(uint16_t rowA, uint16_t rowB, EntityContainers& recs) {
1305 // If there are at least two different entities inside to swap
1306 if GAIA_UNLIKELY (m_header.count <= 1 || rowA == rowB)
1307 return;
1308
1309 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1310
1311 // Update entity data
1312 auto ev = entity_view_mut();
1313 const auto entityA = ev[rowA];
1314 const auto entityB = ev[rowB];
1315
1316 auto& ecA = recs[entityA];
1317 auto& ecB = recs[entityB];
1318 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1319 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1320
1321 ev[rowA] = entityB;
1322 ev[rowB] = entityA;
1323
1324 // Swap component data
1325 auto recView = comp_rec_view();
1326 GAIA_FOR(m_header.genEntities) {
1327 const auto& rec = recView[i];
1328 if (!component_uses_table_storage(rec.comp))
1329 continue;
1330
1331 GAIA_ASSERT(rec.pData == comp_ptr_mut(i));
1332 rec.pItem->swap(rec.pData, rec.pData, rowA, rowB, capacity(), capacity());
1333 }
1334
1335 // Update indices in entity container.
1336 ecA.row = rowB;
1337 ecB.row = rowA;
1338 ecA.pEntity = &ev[rowB];
1339 ecB.pEntity = &ev[rowA];
1340 }
1341
1348 static void swap_chunk_entities(World& world, Entity entityA, Entity entityB) {
1349 // Don't swap if the two entities are the same
1350 if GAIA_UNLIKELY (entityA == entityB)
1351 return;
1352
1353 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1354
1355 auto& ecA = fetch_mut(world, entityA);
1356 auto& ecB = fetch_mut(world, entityB);
1357
1358 // Make sure the two entities are in the same archetype
1359 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1360 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1361
1362 auto* pChunkA = ecA.pChunk;
1363 auto* pChunkB = ecB.pChunk;
1364
1365 // Swap entities in the entity data part
1366 pChunkA->entity_view_mut()[ecA.row] = entityB;
1367 pChunkB->entity_view_mut()[ecB.row] = entityA;
1368
1369 // Swap component data
1370 auto recViewA = pChunkA->comp_rec_view();
1371 GAIA_FOR(pChunkA->m_header.genEntities) {
1372 const auto& recA = recViewA[i];
1373 if (!component_uses_table_storage(recA.comp))
1374 continue;
1375
1376 auto* pDataA = pChunkA->comp_rec_view()[i].pData;
1377 auto* pDataB = pChunkB->comp_rec_view()[i].pData;
1378 recA.pItem->swap(
1379 // Data pointers
1380 pDataA, pDataB,
1381 // Rows
1382 ecA.row, ecB.row,
1383 // Chunk capacities
1384 pChunkA->capacity(), pChunkA->capacity() //
1385 );
1386 }
1387
1388 // Update indices and chunks in entity container.
1389 core::swap(ecA.row, ecB.row);
1390 core::swap(ecA.pChunk, ecB.pChunk);
1391 ecA.pEntity = &ecA.pChunk->entity_view()[ecA.row];
1392 ecB.pEntity = &ecB.pChunk->entity_view()[ecB.row];
1393 }
1394
1399 void enable_entity(uint16_t row, bool enableEntity, EntityContainers& recs) {
1400 GAIA_ASSERT(row < m_header.count && "Entity chunk row out of bounds!");
1401
1402 if (enableEntity) {
1403 // Nothing to enable if there are no disabled entities
1404 if (!m_header.has_disabled_entities())
1405 return;
1406 // Trying to enable an already enabled entity
1407 if (enabled(row))
1408 return;
1409 // Try swapping our entity with the last disabled one
1410 const auto entity = entity_view()[row];
1411 swap_chunk_entities(--m_header.rowFirstEnabledEntity, row, recs);
1412 recs[entity].data.dis = 0;
1413 ++m_header.countEnabled;
1414 } else {
1415 // Nothing to disable if there are no enabled entities
1416 if (!m_header.has_enabled_entities())
1417 return;
1418 // Trying to disable an already disabled entity
1419 if (!enabled(row))
1420 return;
1421 // Try swapping our entity with the last one in our chunk
1422 const auto entity = entity_view()[row];
1423 swap_chunk_entities(m_header.rowFirstEnabledEntity++, row, recs);
1424 recs[entity].data.dis = 1;
1425 --m_header.countEnabled;
1426 }
1427 }
1428
1432 bool enabled(uint16_t row) const {
1433 GAIA_ASSERT(m_header.count > 0);
1434
1435 return row >= (uint16_t)m_header.rowFirstEnabledEntity;
1436 }
1437
1441 uint8_t& data(uint32_t offset) {
1442 return m_data[offset];
1443 }
1444
1448 const uint8_t& data(uint32_t offset) const {
1449 return m_data[offset];
1450 }
1451
1452 //----------------------------------------------------------------------
1453 // Component handling
1454 //----------------------------------------------------------------------
1455
1460 void call_ctor(uint32_t entIdx, uint32_t compIdx, const ComponentCacheItem& item) {
1461 if (item.func_ctor == nullptr || !component_uses_table_storage(item.comp))
1462 return;
1463
1464 GAIA_PROF_SCOPE(Chunk::call_ctor);
1465
1466 auto* pSrc = (void*)comp_ptr_mut(compIdx, entIdx);
1467 item.func_ctor(pSrc, 1);
1468 }
1469
1473 void call_gen_ctors(uint32_t entIdx, uint32_t entCnt) {
1474 if (!m_header.hasAnyCustomGenCtor)
1475 return;
1476
1477 GAIA_PROF_SCOPE(Chunk::call_gen_ctors);
1478
1479 auto recs = comp_rec_view();
1480 GAIA_FOR(m_header.genEntities) {
1481 const auto& rec = recs[i];
1482 if (!component_uses_table_storage(rec.comp))
1483 continue;
1484
1485 const auto* pItem = rec.pItem;
1486 if (pItem == nullptr || pItem->func_ctor == nullptr)
1487 continue;
1488
1489 auto* pSrc = (void*)comp_ptr_mut(i, entIdx);
1490 pItem->func_ctor(pSrc, entCnt);
1491 }
1492 }
1493
1496 if (!m_header.hasAnyCustomGenDtor && !m_header.hasAnyCustomUniCtor)
1497 return;
1498
1499 GAIA_PROF_SCOPE(Chunk::call_all_dtors);
1500
1501 auto ids = ids_view();
1502 auto recs = comp_rec_view();
1503 const auto recs_cnt = recs.size();
1504 GAIA_FOR(recs_cnt) {
1505 const auto& rec = recs[i];
1506 if (!component_uses_table_storage(rec.comp))
1507 continue;
1508
1509 const auto* pItem = rec.pItem;
1510 if (pItem == nullptr || pItem->func_dtor == nullptr)
1511 continue;
1512
1513 auto* pSrc = (void*)comp_ptr_mut(i, 0);
1514 const auto e = ids[i];
1515 const auto cnt = (e.kind() == EntityKind::EK_Gen) ? m_header.count : (uint16_t)1;
1516 pItem->func_dtor(pSrc, cnt);
1517 }
1518 };
1519
1520 //----------------------------------------------------------------------
1521 // Check component presence
1522 //----------------------------------------------------------------------
1523
1527 GAIA_NODISCARD bool has(Entity entity) const {
1528 auto ids = ids_view();
1529 return core::has(ids, entity);
1530 }
1531
1535 template <typename T>
1536 GAIA_NODISCARD bool has() const {
1537 if constexpr (is_pair<T>::value) {
1538 const auto rel = m_header.cc->get<typename T::rel>().entity;
1539 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1540 return has((Entity)Pair(rel, tgt));
1541 } else {
1542 const auto* pComp = m_header.cc->find<T>();
1543 return pComp != nullptr && has(pComp->entity);
1544 }
1545 }
1546
1547 //----------------------------------------------------------------------
1548 // Set component data
1549 //----------------------------------------------------------------------
1550
1556 template <typename T>
1557 decltype(auto) set(uint16_t row) {
1558 verify_comp<T>();
1559
1560 GAIA_ASSERT2(
1561 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1562 "Set providing a row can only be used with generic components");
1563
1564 // Update the world version
1565 ::gaia::ecs::update_version(m_header.worldVersion);
1566
1567 GAIA_ASSERT(row < m_header.capacity);
1568 world_notify_on_set(*const_cast<World*>(m_header.world), comp_entity<T>(), *this, row, (uint16_t)(row + 1));
1569 return view_mut<T>()[row];
1570 }
1571
1577 template <typename T>
1578 decltype(auto) set_idx(uint16_t row, uint32_t compIdx) {
1579 verify_comp<T>();
1580
1581 GAIA_ASSERT2(
1582 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1583 "Set providing a row can only be used with generic components");
1584
1585 // Update the world version
1586 ::gaia::ecs::update_version(m_header.worldVersion);
1587
1588 world_notify_on_set(
1589 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, row, (uint16_t)(row + 1));
1590 return comp_mut_idx<T, true>(row, compIdx);
1591 }
1592
1597 template <typename T>
1598 decltype(auto) set_idx(uint32_t compIdx) {
1599 verify_comp<T>();
1600 static_assert(
1601 entity_kind_v<T> != EntityKind::EK_Gen,
1602 "Set not providing a row can only be used with non-generic components");
1603
1604 // Update the world version
1605 ::gaia::ecs::update_version(m_header.worldVersion);
1606
1607 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, 0, 1);
1608 return comp_mut_idx<T, true>(0, compIdx);
1609 }
1610
1616 template <typename T>
1617 decltype(auto) set(uint16_t row, Entity type) {
1618 const uint32_t compIdx = comp_idx(type);
1619 GAIA_ASSERT2(
1620 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1621 "Set providing a row can only be used with generic components");
1622 GAIA_ASSERT(m_records.pRecords[compIdx].pItem != nullptr);
1623 GAIA_ASSERT(m_records.pRecords[compIdx].pItem->entity.kind() == actual_type_t<T>::Kind);
1624
1625 // Update the world version
1626 ::gaia::ecs::update_version(m_header.worldVersion);
1627
1628 GAIA_ASSERT(row < m_header.capacity);
1629 world_notify_on_set(*const_cast<World*>(m_header.world), type, *this, row, (uint16_t)(row + 1));
1630 return comp_mut_idx<T, true>(row, compIdx);
1631 }
1632
1639 template <typename T>
1640 decltype(auto) sset(uint16_t row) {
1641 GAIA_ASSERT2(
1642 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1643 "Set providing a row can only be used with generic components");
1644
1645 GAIA_ASSERT(row < m_header.capacity);
1646 return sview_mut<T>()[row];
1647 }
1648
1652 template <typename T>
1653 decltype(auto) sset_idx(uint16_t row, uint32_t compIdx) {
1654 verify_comp<T>();
1655
1656 GAIA_ASSERT2(
1657 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1658 "Set providing a row can only be used with generic components");
1659
1660 return comp_mut_idx<T, false>(row, compIdx);
1661 }
1662
1666 template <typename T>
1667 decltype(auto) sset_idx(uint32_t compIdx) {
1668 verify_comp<T>();
1669 static_assert(
1670 entity_kind_v<T> != EntityKind::EK_Gen,
1671 "SSet not providing a row can only be used with non-generic components");
1672
1673 return comp_mut_idx<T, false>(0, compIdx);
1674 }
1675
1683 template <typename T>
1684 decltype(auto) sset(uint16_t row, Entity type) {
1685 static_assert(core::is_raw_v<T>);
1686
1687 const uint32_t compIdx = comp_idx(type);
1688 GAIA_ASSERT2(
1689 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1690 "Set providing a row can only be used with generic components");
1691 GAIA_ASSERT(m_records.pRecords[compIdx].pItem != nullptr);
1692 GAIA_ASSERT(m_records.pRecords[compIdx].pItem->entity.kind() == actual_type_t<T>::Kind);
1693
1694 GAIA_ASSERT(row < m_header.capacity);
1695 return comp_mut_idx<T, false>(row, compIdx);
1696 }
1697
1698 //----------------------------------------------------------------------
1699 // Read component data
1700 //----------------------------------------------------------------------
1701
1708 template <typename T>
1709 GAIA_NODISCARD decltype(auto) get(uint16_t row) const {
1710 static_assert(
1711 actual_type_t<T>::Kind == EntityKind::EK_Gen,
1712 "Get providing a row can only be used with generic components");
1713
1714 return comp_inter<T>(row);
1715 }
1716
1722 template <typename T>
1723 GAIA_NODISCARD decltype(auto) get_idx(uint16_t row, uint32_t compIdx) const {
1724 static_assert(
1725 actual_type_t<T>::Kind == EntityKind::EK_Gen,
1726 "Get providing a row can only be used with generic components");
1727
1728 return comp_inter_idx<T>(row, compIdx);
1729 }
1730
1737 template <typename T>
1738 GAIA_NODISCARD decltype(auto) get(uint16_t row, Entity type) const {
1739 GAIA_ASSERT(row < m_header.count);
1740 const uint32_t compIdx = comp_idx(type);
1741 GAIA_ASSERT2(
1742 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1743 "Get providing a row can only be used with generic components");
1744 GAIA_ASSERT(m_records.pRecords[compIdx].pItem != nullptr);
1745 GAIA_ASSERT(m_records.pRecords[compIdx].pItem->entity.kind() == actual_type_t<T>::Kind);
1746 return comp_inter_idx<T>(row, compIdx);
1747 }
1748
1753 template <typename T>
1754 GAIA_NODISCARD decltype(auto) get() const {
1755 static_assert(
1756 actual_type_t<T>::Kind != EntityKind::EK_Gen,
1757 "Get not providing a row can only be used with non-generic components");
1758
1759 return comp_inter<T>(0);
1760 }
1761
1766 template <typename T>
1767 GAIA_NODISCARD decltype(auto) get_idx(uint32_t compIdx) const {
1768 static_assert(
1769 actual_type_t<T>::Kind != EntityKind::EK_Gen,
1770 "Get not providing a row can only be used with non-generic components");
1771
1772 return comp_inter_idx<T>(0, compIdx);
1773 }
1774
1778 template <typename T>
1779 GAIA_NODISCARD Entity comp_entity() const {
1780 if constexpr (is_pair<T>::value) {
1781 const auto rel = m_header.cc->get<typename T::rel>().entity;
1782 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1783 return (Entity)Pair(rel, tgt);
1784 } else {
1785 return m_header.cc->get<T>().entity;
1786 }
1787 }
1788
1793 GAIA_NODISCARD uint32_t comp_idx(Entity entity) const {
1794 return ecs::comp_idx<ChunkHeader::MAX_COMPONENTS>(m_records.pCompEntities, entity);
1795 }
1796
1802 GAIA_NODISCARD uint32_t comp_idx(Entity entity, uint32_t offset) const {
1803 return ecs::comp_idx({m_records.pCompEntities + offset, m_header.count - offset}, entity);
1804 }
1805
1806 //----------------------------------------------------------------------
1807
1809 void set_idx(uint32_t value) {
1810 m_header.index = value;
1811 }
1812
1815 GAIA_NODISCARD uint32_t idx() const {
1816 return m_header.index;
1817 }
1818
1821 GAIA_NODISCARD bool has_enabled_entities() const {
1822 return m_header.has_enabled_entities();
1823 }
1824
1827 GAIA_NODISCARD bool has_disabled_entities() const {
1828 return m_header.has_disabled_entities();
1829 }
1830
1833 GAIA_NODISCARD bool dying() const {
1834 return m_header.lifespanCountdown > 0;
1835 }
1836
1839 GAIA_NODISCARD bool queued_for_deletion() const {
1840 return m_header.deleteQueueIndex != BadIndex;
1841 }
1842
1845 GAIA_NODISCARD uint32_t delete_queue_index() const {
1846 return m_header.deleteQueueIndex;
1847 }
1848
1850 void delete_queue_index(uint32_t idx) {
1851 m_header.deleteQueueIndex = idx;
1852 }
1853
1856 m_header.deleteQueueIndex = BadIndex;
1857 }
1858
1860 void die() {
1861 m_header.dead = 1;
1862 }
1863
1866 GAIA_NODISCARD bool dead() const {
1867 return m_header.dead == 1;
1868 }
1869
1872 GAIA_ASSERT(!dead());
1873 GAIA_ASSERT(!queued_for_deletion());
1874 m_header.lifespanCountdown = ChunkHeader::MAX_CHUNK_LIFESPAN;
1875 }
1876
1878 void revive() {
1879 GAIA_ASSERT(!dead());
1880 m_header.lifespanCountdown = 0;
1881 clear_delete_queue_index();
1882 }
1883
1887 GAIA_ASSERT(dying());
1888 --m_header.lifespanCountdown;
1889 return dying();
1890 }
1891
1894 GAIA_NODISCARD bool full() const {
1895 return m_header.count >= m_header.capacity;
1896 }
1897
1900 GAIA_NODISCARD bool is_semi() const {
1901 // We want the chunk filled to at least 75% before considering it semi-full
1902 constexpr float Threshold = 0.75f;
1903 return ((float)m_header.count / (float)m_header.capacity) < Threshold;
1904 }
1905
1908 GAIA_NODISCARD uint16_t size() const {
1909 return m_header.count;
1910 }
1911
1914 GAIA_NODISCARD bool empty() const {
1915 return m_header.count == 0;
1916 }
1917
1920 GAIA_NODISCARD uint16_t size_enabled() const {
1921 return m_header.countEnabled;
1922 }
1923
1926 GAIA_NODISCARD uint16_t size_disabled() const {
1927 return (uint16_t)m_header.rowFirstEnabledEntity;
1928 }
1929
1932 GAIA_NODISCARD uint16_t capacity() const {
1933 return m_header.capacity;
1934 }
1935
1938 GAIA_NODISCARD uint8_t size_generic() const {
1939 return m_header.genEntities;
1940 }
1941
1947 GAIA_NODISCARD bool changed(uint32_t requiredVersion) const {
1948 const auto* versions = m_records.pVersions;
1949 const auto changeVersion = versions[0];
1950 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1951 }
1952
1957 GAIA_NODISCARD bool changed(uint32_t requiredVersion, uint32_t compIdx) const {
1958 const auto* versions = m_records.pVersions;
1959 // Do +1 because index 0 is reserved for the entity version number.
1960 const auto changeVersion = versions[compIdx + 1];
1961 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1962 }
1963
1968 GAIA_NODISCARD bool entity_order_changed(uint32_t requiredVersion) const {
1969 return ::gaia::ecs::version_changed(m_header.entityOrderVersion, requiredVersion);
1970 }
1971
1973 GAIA_FORCEINLINE void update_world_version(uint32_t compIdx) {
1974 auto versions = comp_version_view_mut();
1975 // Automatically treat the entity as changed.
1976 versions[0] = m_header.worldVersion;
1977 // Do +1 because index 0 is reserved for the entity version number.
1978 versions[compIdx + 1] = m_header.worldVersion;
1979 // Sorted queries keyed by this component can invalidate their cached order immediately.
1980 world_invalidate_sorted_queries_for_entity(
1981 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx]);
1982 }
1983
1985 GAIA_FORCEINLINE void update_entity_order_version() {
1986 m_header.entityOrderVersion = m_header.worldVersion;
1987 // Row-order changes invalidate cached sorted slices regardless of sort key.
1988 world_invalidate_sorted_queries(*const_cast<World*>(m_header.world));
1989 }
1990
1992 GAIA_FORCEINLINE void update_world_version() {
1993 // Edit the version pointer directly. The first elements is always the entity version.
1994 // This area of memory is always present.
1995 auto* versions = m_records.pVersions;
1996 // We update the version of the entity only. If this one changes,
1997 // all other components are considered changed as well.
1998 versions[0] = m_header.worldVersion;
1999 }
2000
2002 GAIA_FORCEINLINE void update_world_version_init() {
2003 auto* versions = m_records.pVersions;
2004 // We update the version of the entity and all components to match the world version.
2005 versions[0] = m_header.worldVersion;
2006 GAIA_FOR(m_header.genEntities) versions[1 + i] = m_header.worldVersion;
2007 m_header.entityOrderVersion = m_header.worldVersion;
2008 }
2009
2011 void diag() const {
2012 GAIA_LOG_N(
2013 " Chunk #%04u, entities:%u/%u, lifespanCountdown:%u", m_header.index, m_header.count, m_header.capacity,
2014 m_header.lifespanCountdown);
2015 }
2016 };
2017 } // namespace ecs
2018} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:27
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:907
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:832
GAIA_NODISCARD uint16_t capacity() const
Returns the number of entities in the chunk.
Definition chunk.h:1932
GAIA_NODISCARD decltype(auto) get_idx(uint32_t compIdx) const
Returns the value stored in the unique component T using a pre-resolved component column.
Definition chunk.h:1767
static void free(Chunk *pChunk)
Releases all memory allocated by pChunk.
Definition chunk.h:509
const uint8_t & data(uint32_t offset) const
Returns an immutable pointer to chunk data.
Definition chunk.h:1448
GAIA_NODISCARD decltype(auto) view() const
Returns a read-only entity or component view.
Definition chunk.h:646
decltype(auto) set_idx(uint16_t row, uint32_t compIdx)
Sets the value of a generic component using a pre-resolved component column.
Definition chunk.h:1578
decltype(auto) sset(uint16_t row, Entity type)
Sets the value of a generic entity type at the position row in the chunk.
Definition chunk.h:1684
void save(ser::serializer &s) const
Serializes chunk contents: entity counts, lifespan state, entity ids and component data.
Definition chunk.h:529
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:1270
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:963
GAIA_FORCEINLINE void update_world_version()
Update the version of all components.
Definition chunk.h:1992
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:1802
GAIA_NODISCARD Entity comp_entity() const
Component entity for the chunk archetype contents.
Definition chunk.h:1779
bool progress_death()
Updates internal lifespan.
Definition chunk.h:1886
decltype(auto) set(uint16_t row)
Sets the value of the unique component T on row in the chunk.
Definition chunk.h:1557
GAIA_NODISCARD World & world()
Owning world mutable reference.
Definition chunk.h:856
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:343
void die()
Marks the chunk as dead (ready to delete)
Definition chunk.h:1860
GAIA_NODISCARD EntitySpan ids_view() const
Span over the component and entity identifiers held by this chunk.
Definition chunk.h:868
GAIA_NODISCARD uint32_t idx() const
Returns the index of this chunk in its archetype's storage.
Definition chunk.h:1815
uint8_t & data(uint32_t offset)
Returns a mutable pointer to chunk data.
Definition chunk.h:1441
void call_all_dtors()
Invokes registered destructors for all custom component instances before release.
Definition chunk.h:1495
GAIA_NODISCARD decltype(auto) sview_auto()
Returns an automatically-typed mutable view without query-version updates.
Definition chunk.h:844
decltype(auto) sset_idx(uint16_t row, uint32_t compIdx)
Sets the value of a generic component using a pre-resolved component column.
Definition chunk.h:1653
void update_versions()
Updates the version numbers for this chunk.
Definition chunk.h:619
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:725
GAIA_NODISCARD decltype(auto) sview_mut()
Returns a mutable entity or component view without query-version updates.
Definition chunk.h:736
static constexpr uint16_t chunk_header_size()
Size in bytes of the chunk header area reserved before entity and component data.
Definition chunk.h:440
GAIA_FORCEINLINE void update_world_version(uint32_t compIdx)
Update the version of a component at the index.
Definition chunk.h:1973
GAIA_NODISCARD bool has_disabled_entities() const
Checks is this chunk has any disabled entities.
Definition chunk.h:1827
GAIA_NODISCARD bool full() const
Checks is the full capacity of the has has been reached.
Definition chunk.h:1894
GAIA_NODISCARD bool is_semi() const
Checks is the chunk is semi-full.
Definition chunk.h:1900
GAIA_NODISCARD const World & world() const
Owning world const reference.
Definition chunk.h:862
GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const
Returns a read-only entity or component view.
Definition chunk.h:632
GAIA_NODISCARD uint16_t add_entity(Entity entity)
Make.
Definition chunk.h:914
GAIA_NODISCARD uint16_t size_enabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1920
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:807
static constexpr uint16_t chunk_total_bytes(uint16_t dataSize)
Total chunk allocation size for a given usable data size.
Definition chunk.h:454
void start_dying()
Starts the process of dying (not yet ready to delete, can be revived)
Definition chunk.h:1871
GAIA_NODISCARD bool has() const
Checks if component T is present in the chunk.
Definition chunk.h:1536
GAIA_NODISCARD uint16_t size() const
Returns the total number of entities in the chunk (both enabled and disabled)
Definition chunk.h:1908
GAIA_FORCEINLINE void update_entity_order_version()
Updates the entity-order version after rows were added, removed, or reordered.
Definition chunk.h:1985
GAIA_NODISCARD decltype(auto) view_auto()
Returns an automatically-typed mutable view over the chunk.
Definition chunk.h:819
GAIA_NODISCARD decltype(auto) get(uint16_t row) const
Returns the value stored in the generic component T on row in the chunk.
Definition chunk.h:1709
GAIA_NODISCARD uint32_t delete_queue_index() const
Returns the index inside World's deferred chunk-delete queue.
Definition chunk.h:1845
void call_gen_ctors(uint32_t entIdx, uint32_t entCnt)
Invokes registered constructors for all generic columns at a row range.
Definition chunk.h:1473
void load(ser::serializer &s)
Deserializes chunk contents, restoring entity ids and component data.
Definition chunk.h:564
static uintptr_t chunk_data_area_offset()
Returns the relative offset of m_data in Chunk.
Definition chunk.h:467
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:1054
decltype(auto) set_idx(uint32_t compIdx)
Sets the value of a unique component using a pre-resolved component column.
Definition chunk.h:1598
void set_idx(uint32_t value)
Sets the index of this chunk in its archetype's storage.
Definition chunk.h:1809
GAIA_NODISCARD std::span< const ComponentRecord > comp_rec_view() const
Span over the component records describing each chunk column.
Definition chunk.h:874
void clear_delete_queue_index()
Clears the deferred chunk-delete queue index.
Definition chunk.h:1855
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:1968
void call_ctor(uint32_t entIdx, uint32_t compIdx, const ComponentCacheItem &item)
Invokes the registered constructor for one component instance.
Definition chunk.h:1460
static constexpr uint16_t chunk_data_bytes(uint16_t totalSize)
Usable data area size for a given total chunk allocation size.
Definition chunk.h:461
GAIA_NODISCARD decltype(auto) get() const
Returns the value stored in the unique component T.
Definition chunk.h:1754
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:1957
void delete_queue_index(uint32_t idx)
Stores the index inside World's deferred chunk-delete queue.
Definition chunk.h:1850
decltype(auto) sset_idx(uint32_t compIdx)
Sets the value of a unique component using a pre-resolved component column.
Definition chunk.h:1667
static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers &recs)
Copies all data associated with srcEntity into dstEntity.
Definition chunk.h:930
bool enabled(uint16_t row) const
Checks if the entity is enabled.
Definition chunk.h:1432
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:693
GAIA_NODISCARD bool dying() const
Checks is this chunk is dying.
Definition chunk.h:1833
GAIA_NODISCARD uint8_t * comp_ptr_mut(uint32_t compIdx)
Mutable pointer to the start of a component column.
Definition chunk.h:881
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:1947
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:606
GAIA_NODISCARD uint16_t size_disabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1926
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:1304
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:1142
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:1399
GAIA_NODISCARD uint32_t comp_idx(Entity entity) const
Returns the internal index of a component based on the provided entity.
Definition chunk.h:1793
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:1348
GAIA_FORCEINLINE void update_world_version_init()
Update the version of all components on chunk init.
Definition chunk.h:2002
GAIA_NODISCARD bool dead() const
Checks is this chunk is dead (ready to delete)
Definition chunk.h:1866
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:890
decltype(auto) set(uint16_t row, Entity type)
Sets the value of a generic entity type at the position row in the chunk.
Definition chunk.h:1617
GAIA_NODISCARD bool has_enabled_entities() const
Checks is this chunk has any enabled entities.
Definition chunk.h:1821
GAIA_NODISCARD const uint8_t * comp_ptr(uint32_t compIdx) const
Const pointer to the start of a component column.
Definition chunk.h:898
void revive()
Makes a dying chunk alive again.
Definition chunk.h:1878
GAIA_NODISCARD bool has(Entity entity) const
Checks if a component/entity entity is present in the chunk.
Definition chunk.h:1527
GAIA_NODISCARD bool queued_for_deletion() const
Returns true when the chunk is currently queued for deferred deletion.
Definition chunk.h:1839
GAIA_NODISCARD bool empty() const
Checks is there are any entities in the chunk.
Definition chunk.h:1914
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:1079
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:1206
GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to)
Returns a mutable entity or component view.
Definition chunk.h:668
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:360
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:708
void diag() const
Logs a diagnostic line describing the chunk capacity and lifespan state.
Definition chunk.h:2011
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:656
GAIA_NODISCARD decltype(auto) get_idx(uint16_t row, uint32_t compIdx) const
Returns the value stored in the generic component T using a pre-resolved component column.
Definition chunk.h:1723
GAIA_NODISCARD uint8_t size_generic() const
Returns the total number of generic entities/components in the chunk.
Definition chunk.h:1938
GAIA_NODISCARD decltype(auto) get(uint16_t row, Entity type) const
Returns the value stored in the generic component type on row in the chunk.
Definition chunk.h:1738
static Chunk * create(const World &wld, const ComponentCache &cc, uint32_t chunkIndex, uint16_t capacity, uint8_t cntEntities, uint8_t genEntities, 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:478
GAIA_NODISCARD EntitySpan entity_view() const
Span over the entities stored in this chunk.
Definition chunk.h:850
GAIA_NODISCARD decltype(auto) view_mut()
Returns a mutable entity or component view.
Definition chunk.h:683
decltype(auto) sset(uint16_t row)
Sets the value of the unique component T on row in the chunk.
Definition chunk.h:1640
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:996
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:750
Owns entities, components, archetypes, queries, observers, and systems.
Definition world.h:80
Wrapper for two Entities forming a relationship pair.
Definition id.h:614
Strict weak ordering functor using operator<.
Definition utility.h:1380
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:296
Detects whether a type is a relationship pair.
Definition id.h:285
Read-only view for a selected layout and item type.
Definition data_layout_policy.h:129
Mutable view for a selected layout and item type.
Definition data_layout_policy.h:134
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