Gaia-ECS v0.9.3
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
35 class GAIA_API Chunk final {
36 public:
40
41 private:
43 ChunkHeader m_header;
45 ChunkRecords m_records;
46
57 uint8_t m_data[1];
58
59 GAIA_MSVC_WARNING_PUSH()
60 GAIA_MSVC_WARNING_DISABLE(26495)
61
62 // Hidden default constructor. Only use to calculate the relative offset of m_data
63 Chunk() = default;
64
65 Chunk(
66 const World& wld, const ComponentCache& cc, //
67 uint32_t chunkIndex, uint16_t capacity, uint8_t genEntities, //
68 uint32_t& worldVersion): //
69 m_header(wld, cc, chunkIndex, capacity, genEntities, worldVersion) {
70 // Chunk data area consist of memory offsets, entities, and component data. Normally, we would need
71 // to in-place construct all of it manually.
72 // However, the memory offsets and entities are all trivial types and components are initialized via
73 // their constructors on-demand (if not trivial) so we do not really need to do any construction here.
74 }
75
76 GAIA_MSVC_WARNING_POP()
77
78 GAIA_CLANG_WARNING_PUSH()
79 // Memory is aligned so we can silence this warning
80 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
81
82 void init(
83 uint32_t cntEntities, const Entity* ids, const ComponentCacheItem* const* pItems,
84 const ChunkDataOffsets& headerOffsets, const ChunkDataOffset* compOffs) {
85 m_header.cntEntities = (uint8_t)cntEntities;
86
87 // Cache pointers to versions
88 m_records.pVersions = (ComponentVersion*)&data(headerOffsets.firstByte_Versions);
89
90 // Cache entity ids
91 if (cntEntities > 0) {
92 auto* dst = m_records.pCompEntities = (Entity*)&data(headerOffsets.firstByte_CompEntities);
93
94 // We treat the entity array as if were MAX_COMPONENTS long.
95 // Real size can be smaller.
96 uint32_t j = 0;
97 for (; j < cntEntities; ++j)
98 dst[j] = ids[j];
99 for (; j < ChunkHeader::MAX_COMPONENTS; ++j)
100 dst[j] = EntityBad;
101 }
102
103 // Cache component records
104 if (cntEntities > 0) {
105 auto* dst = m_records.pRecords = (ComponentRecord*)&data(headerOffsets.firstByte_Records);
106 GAIA_FOR_(cntEntities, j) {
107 dst[j].comp =
108 pItems[j] == nullptr ? Component(IdentifierIdBad, 0, 0, 0, DataStorageType::Table) : pItems[j]->comp;
109 dst[j].pData = &data(compOffs[j]);
110 dst[j].pItem = pItems[j];
111 }
112 }
113
114 m_records.pEntities = (Entity*)&data(headerOffsets.firstByte_EntityData);
115
116 // Now that records are set, we use the cached component descriptors to set ctor/dtor masks.
117 {
118 auto recs = comp_rec_view();
119 const auto recs_cnt = recs.size();
120 GAIA_FOR(recs_cnt) {
121 const auto& rec = recs[i];
122 if (!component_has_inline_data(rec.comp))
123 continue;
124
125 const auto e = m_records.pCompEntities[i];
126 if (e.kind() == EntityKind::EK_Gen) {
127 m_header.hasAnyCustomGenCtor |= (rec.pItem->func_ctor != nullptr);
128 m_header.hasAnyCustomGenDtor |= (rec.pItem->func_dtor != nullptr);
129 } else {
130 m_header.hasAnyCustomUniCtor |= (rec.pItem->func_ctor != nullptr);
131 m_header.hasAnyCustomUniDtor |= (rec.pItem->func_dtor != nullptr);
132
133 // We construct unique components right away if possible
134 call_ctor(0, *rec.pItem);
135 }
136 }
137 }
138
139 // Make sure world versions are set initially.
140 update_world_version_init();
141 }
142
143 GAIA_CLANG_WARNING_POP()
144
145
147 GAIA_NODISCARD std::span<const ComponentVersion> comp_version_view() const {
148 return {(const ComponentVersion*)m_records.pVersions, (size_t)m_header.cntEntities + 1};
149 }
150
153 GAIA_NODISCARD std::span<ComponentVersion> comp_version_view_mut() {
154 return {m_records.pVersions, (size_t)m_header.cntEntities + 1};
155 }
156
157 GAIA_NODISCARD std::span<Entity> entity_view_mut() {
158 return {m_records.pEntities, m_header.count};
159 }
160
168 template <typename T>
169 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) const //
170 -> decltype(std::span<const uint8_t>{}) {
171
172 if constexpr (std::is_same_v<core::raw_t<T>, Entity>) {
173 GAIA_ASSERT(to <= m_header.count);
174 return {(const uint8_t*)&m_records.pEntities[from], to - from};
175 } else if constexpr (is_pair<T>::value) {
176 using TT = typename T::type;
177 using U = typename component_type_t<TT>::Type;
178 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
179
180 constexpr auto kind = entity_kind_v<TT>;
181
182 if constexpr (mem::is_soa_layout_v<U>) {
183 GAIA_ASSERT(from == 0);
184 GAIA_ASSERT(to == capacity());
185 return {comp_ptr(compIdx), to};
186 } else if constexpr (kind == EntityKind::EK_Gen) {
187 GAIA_ASSERT(to <= m_header.count);
188 return {comp_ptr(compIdx, from), to - from};
189 } else {
190 GAIA_ASSERT(to <= m_header.count);
191 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
192 return {comp_ptr(compIdx), 1};
193 }
194 } else {
195 using U = typename component_type_t<T>::Type;
196 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
197
198 constexpr auto kind = entity_kind_v<T>;
199
200 if constexpr (mem::is_soa_layout_v<U>) {
201 GAIA_ASSERT(from == 0);
202 GAIA_ASSERT(to == capacity());
203 return {comp_ptr(compIdx), to};
204 } else if constexpr (kind == EntityKind::EK_Gen) {
205 GAIA_ASSERT(to <= m_header.count);
206 return {comp_ptr(compIdx, from), to - from};
207 } else {
208 GAIA_ASSERT(to <= m_header.count);
209 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
210 return {comp_ptr(compIdx), 1};
211 }
212 }
213 }
214
221 template <typename T>
222 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter(uint32_t from, uint32_t to) const //
223 -> decltype(std::span<const uint8_t>{}) {
224 if constexpr (std::is_same_v<core::raw_t<T>, Entity>)
225 return view_inter_idx<T>(BadIndex, from, to);
226 else if constexpr (is_pair<T>::value) {
227 const auto rel = m_header.cc->get<typename T::rel>().entity;
228 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
229 return view_inter_idx<T>(comp_idx((Entity)Pair(rel, tgt)), from, to);
230 } else {
231 const auto comp = m_header.cc->get<T>().entity;
232#if GAIA_ASSERT_ENABLED
233 constexpr auto kind = entity_kind_v<T>;
234 GAIA_ASSERT(comp.kind() == kind);
235#endif
236 return view_inter_idx<T>(comp_idx(comp), from, to);
237 }
238 }
239
248 template <typename T, bool WorldVersionUpdateWanted>
249 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) //
250 -> decltype(std::span<uint8_t>{}) {
251 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "view_mut can't be used to modify Entity");
252
253 if constexpr (is_pair<T>::value) {
254 using TT = typename T::type;
255 using U = typename component_type_t<TT>::Type;
256 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
257
258 constexpr auto kind = entity_kind_v<TT>;
259
260 // Update version number if necessary so we know RW access was used on the chunk
261 if constexpr (WorldVersionUpdateWanted) {
262 update_world_version(compIdx);
263
264#if GAIA_ENABLE_SET_HOOKS
265 const auto& rec = m_records.pRecords[compIdx];
266 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
267 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
268#endif
269 }
270
271 if constexpr (mem::is_soa_layout_v<U>) {
272 GAIA_ASSERT(from == 0);
273 GAIA_ASSERT(to == capacity());
274 return {comp_ptr_mut(compIdx), to};
275 } else if constexpr (kind == EntityKind::EK_Gen) {
276 GAIA_ASSERT(to <= m_header.count);
277 return {comp_ptr_mut(compIdx, from), to - from};
278 } else {
279 GAIA_ASSERT(to <= m_header.count);
280 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
281 return {comp_ptr_mut(compIdx), 1};
282 }
283 } else {
284 using U = typename component_type_t<T>::Type;
285 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
286 constexpr auto kind = entity_kind_v<T>;
287
288 // Update version number if necessary so we know RW access was used on the chunk
289 if constexpr (WorldVersionUpdateWanted) {
290 update_world_version(compIdx);
291
292#if GAIA_ENABLE_SET_HOOKS
293 const auto& rec = m_records.pRecords[compIdx];
294 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
295 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
296#endif
297 }
298
299 if constexpr (mem::is_soa_layout_v<U>) {
300 GAIA_ASSERT(from == 0);
301 GAIA_ASSERT(to == capacity());
302 return {comp_ptr_mut(compIdx), to};
303 } else if constexpr (kind == EntityKind::EK_Gen) {
304 GAIA_ASSERT(to <= m_header.count);
305 return {comp_ptr_mut(compIdx, from), to - from};
306 } else {
307 GAIA_ASSERT(to <= m_header.count);
308 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
309 return {comp_ptr_mut(compIdx), 1};
310 }
311 }
312 }
313
314 template <typename T, bool WorldVersionUpdateWanted>
315 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter(uint32_t from, uint32_t to) //
316 -> decltype(std::span<uint8_t>{}) {
317 if constexpr (is_pair<T>::value) {
318 const auto rel = m_header.cc->get<typename T::rel>().entity;
319 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
320 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx((Entity)Pair(rel, tgt)), from, to);
321 } else {
322 const auto comp = m_header.cc->get<T>().entity;
323#if GAIA_ASSERT_ENABLED
324 constexpr auto kind = entity_kind_v<T>;
325 GAIA_ASSERT(comp.kind() == kind);
326#endif
327 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx(comp), from, to);
328 }
329 }
330
331 public:
338 template <bool WorldVersionUpdateWanted>
339 GAIA_NODISCARD GAIA_FORCEINLINE auto comp_ptr_mut_gen(uint32_t compIdx, uint32_t row) {
340 // Update version number if necessary so we know RW access was used on the chunk
341 if constexpr (WorldVersionUpdateWanted) {
342 update_world_version(compIdx);
343
344#if GAIA_ENABLE_SET_HOOKS
345 const auto& rec = m_records.pRecords[compIdx];
346 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
347 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
348#endif
349 }
350
351 return comp_ptr_mut(compIdx, row);
352 }
353
356 void finish_write(uint32_t compIdx, uint16_t from, uint16_t to) {
357 GAIA_ASSERT(compIdx < m_header.cntEntities);
358 if (from >= to)
359 return;
360
361 update_world_version(compIdx);
362
363#if GAIA_ENABLE_SET_HOOKS
364 const auto& rec = m_records.pRecords[compIdx];
365 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
366 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
367#endif
368
369 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, from, to);
370 }
371
372 private:
379 template <typename T>
380 GAIA_NODISCARD decltype(auto) comp_inter(uint16_t row) const {
381 using U = typename actual_type_t<T>::Type;
382 using RetValueType = decltype(view<T>()[0]);
383
384 GAIA_ASSERT(row < m_header.count);
385 if constexpr (mem::is_soa_layout_v<U>)
386 return view<T>(0, capacity())[row];
387 else if constexpr (sizeof(RetValueType) <= 8)
388 return view<T>()[row];
389 else
390 return (const U&)view<T>()[row];
391 }
392
393 template <typename T>
394 GAIA_NODISCARD decltype(auto) comp_inter_idx(uint16_t row, uint32_t compIdx) const {
395 using U = typename actual_type_t<T>::Type;
396 using RetValueType = decltype(view_raw<T>((const void*)nullptr, 1)[0]);
397
398 GAIA_ASSERT(row < m_header.count);
399 if constexpr (mem::is_soa_layout_v<U>)
400 return view_raw<T>(comp_ptr(compIdx), capacity())[row];
401 else if constexpr (entity_kind_v<T> == EntityKind::EK_Gen) {
402 if constexpr (sizeof(RetValueType) <= 8)
403 return view_raw<T>(comp_ptr(compIdx, row), 1)[0];
404 else
405 return (const U&)view_raw<T>(comp_ptr(compIdx, row), 1)[0];
406 } else {
407 if constexpr (sizeof(RetValueType) <= 8)
408 return view_raw<T>(comp_ptr(compIdx), 1)[0];
409 else
410 return (const U&)view_raw<T>(comp_ptr(compIdx), 1)[0];
411 }
412 }
413
414 template <typename T, bool WorldVersionUpdateWanted>
415 GAIA_NODISCARD decltype(auto) comp_mut_idx(uint16_t row, uint32_t compIdx) {
416 using U = typename actual_type_t<T>::Type;
417
418 GAIA_ASSERT(row < m_header.capacity);
419 if constexpr (mem::is_soa_layout_v<U>)
420 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), capacity())[row];
421 else if constexpr (entity_kind_v<T> == EntityKind::EK_Gen)
422 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, row), 1)[0];
423 else
424 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), 1)[0];
425 }
426
427 public:
428 Chunk(const Chunk& chunk) = delete;
429 Chunk(Chunk&& chunk) = delete;
430 Chunk& operator=(const Chunk& chunk) = delete;
431 Chunk& operator=(Chunk&& chunk) = delete;
432 ~Chunk() = default;
433
434 static constexpr uint16_t chunk_header_size() {
435 const auto dataAreaOffset =
436 // ChunkAllocator reserves the first few bytes for internal purposes
437 MemoryBlockUsableOffset +
438 // Chunk "header" area (before actual entity/component data starts)
439 sizeof(ChunkHeader) + sizeof(ChunkRecords);
440 static_assert(dataAreaOffset % MemoryBlockAlignment == 0);
441 static_assert(dataAreaOffset < UINT16_MAX);
442 return dataAreaOffset;
443 }
444
445 static constexpr uint16_t chunk_total_bytes(uint16_t dataSize) {
446 return chunk_header_size() + dataSize;
447 }
448
449 static constexpr uint16_t chunk_data_bytes(uint16_t totalSize) {
450 return totalSize - chunk_header_size();
451 }
452
454 static uintptr_t chunk_data_area_offset() {
455 // Note, offsetof is implementation-defined and conditionally-supported since C++17.
456 // Therefore, we instantiate the chunk and calculate the relative address ourselves.
457 Chunk chunk;
458 const auto chunk_offset = (uintptr_t)&chunk;
459 const auto data_offset = (uintptr_t)&chunk.m_data[0];
460 return data_offset - chunk_offset;
461 }
462
465 static Chunk* create(
466 const World& wld, const ComponentCache& cc, //
467 uint32_t chunkIndex, uint16_t capacity, uint8_t cntEntities, uint8_t genEntities, //
468 uint16_t dataBytes, uint32_t& worldVersion,
469 // data offsets
470 const ChunkDataOffsets& offsets,
471 // component entities
472 const Entity* ids,
473 // resolved component storage items
474 const ComponentCacheItem* const* pItems,
475 // component offsets
476 const ChunkDataOffset* compOffs) {
477 const auto totalBytes = chunk_total_bytes(dataBytes);
478#if GAIA_ECS_CHUNK_ALLOCATOR
479 auto* pChunk = (Chunk*)ChunkAllocator::get().alloc(totalBytes);
480 (void)new (pChunk) Chunk(wld, cc, chunkIndex, capacity, genEntities, worldVersion);
481#else
482 GAIA_ASSERT(totalBytes <= MaxMemoryBlockSize);
483 const auto sizeType = mem_block_size_type(totalBytes);
484 const auto allocSize = mem_block_size(sizeType);
485 auto* pChunkMem = mem::AllocHelper::alloc<uint8_t>(allocSize);
486 std::memset(pChunkMem, 0, allocSize);
487 auto* pChunk = new (pChunkMem) Chunk(wld, cc, chunkIndex, capacity, genEntities, worldVersion);
488#endif
489
490 pChunk->init((uint32_t)cntEntities, ids, pItems, offsets, compOffs);
491 return pChunk;
492 }
493
496 static void free(Chunk* pChunk) {
497 GAIA_ASSERT(pChunk != nullptr);
498 GAIA_ASSERT(!pChunk->dead());
499
500 // Mark as dead
501 pChunk->die();
502
503 // Call destructors for components that need it
504 pChunk->call_all_dtors();
505
506 pChunk->~Chunk();
507#if GAIA_ECS_CHUNK_ALLOCATOR
508 ChunkAllocator::get().free(pChunk);
509#else
510 mem::AllocHelper::free((uint8_t*)pChunk);
511#endif
512 }
513
514 void save(ser::serializer& s) const {
515 s.save(m_header.count);
516 if (m_header.count == 0)
517 return;
518
519 s.save(m_header.countEnabled);
520
521 const uint16_t dead = m_header.dead;
522 const uint16_t lifespanCountdown = m_header.lifespanCountdown;
523 s.save(dead);
524 s.save(lifespanCountdown);
525
526 const auto cnt = (uint32_t)m_header.count;
527 const auto cap = (uint32_t)m_header.capacity;
528
529 // Store entity data
530 {
531 const auto* pData = m_records.pEntities;
532 GAIA_FOR(cnt) s.save(pData[i]);
533 }
534
535 // Store component data
536 {
537 for (const auto& rec: comp_rec_view()) {
538 // Skip the component if there's no size associated with it
539 if (!component_has_inline_data(rec.comp))
540 continue;
541
542 rec.pItem->save(s, rec.pData, 0, cnt, cap);
543 }
544 }
545 }
546
547 void load(ser::serializer& s) {
548 uint16_t prevCount = m_header.count;
549 s.load(m_header.count);
550 if (m_header.count == 0)
551 return;
552
553 s.load(m_header.countEnabled);
554
555 uint16_t dead = 0;
556 uint16_t lifespanCountdown = 0;
557 s.load(dead);
558 s.load(lifespanCountdown);
559 m_header.dead = dead != 0;
560 m_header.lifespanCountdown = lifespanCountdown;
561
562 const auto cnt = (uint32_t)m_header.count;
563 const auto cap = (uint32_t)m_header.capacity;
564
565 // Load entity data
566 {
567 GAIA_FOR(cnt) {
568 Entity e;
569 s.load(e);
570 entity_view_mut()[i] = e;
571 }
572 }
573
574 // Load component data. Call constructors first as necessary.
575 call_gen_ctors(prevCount, cnt);
576 {
577 for (const auto& rec: comp_rec_view()) {
578 // Skip the component if there's no size associated with it
579 if (!component_has_inline_data(rec.comp))
580 continue;
581
582 rec.pItem->load(s, rec.pData, 0, cnt, cap);
583 }
584 }
585 }
586
590 // Should never be called over an empty chunk
591 GAIA_ASSERT(!empty());
592
593#if GAIA_ASSERT_ENABLED
594 // Invalidate the entity in chunk data
595 entity_view_mut()[m_header.count - 1] = EntityBad;
596#endif
597
598 --m_header.count;
599 }
600
603 ::gaia::ecs::update_version(m_header.worldVersion);
604 update_world_version();
605 update_entity_order_version();
606 }
607
614 template <typename T>
615 GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const {
616 using U = typename actual_type_t<T>::Type;
617
618 // Always consider full range for SoA
619 if constexpr (mem::is_soa_layout_v<U>)
620 return mem::auto_view_policy_get<U>{view_inter<T>(0, capacity())};
621 else
622 return mem::auto_view_policy_get<U>{view_inter<T>(from, to)};
623 }
624
625 template <typename T>
626 GAIA_NODISCARD decltype(auto) view() const {
627 return view<T>(0, m_header.count);
628 }
629
630 template <typename T>
631 GAIA_NODISCARD decltype(auto) view_raw(const void* ptr, uint32_t size) const {
632 using U = typename actual_type_t<T>::Type;
633 return mem::auto_view_policy_get<U>{std::span{(const uint8_t*)ptr, size}};
634 }
635
642 template <typename T>
643 GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to) {
644 using U = typename actual_type_t<T>::Type;
645 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
646
647 // Always consider full range for SoA
648 if constexpr (mem::is_soa_layout_v<U>)
649 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(0, capacity())};
650 else
651 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(from, to)};
652 }
653
654 template <typename T>
655 GAIA_NODISCARD decltype(auto) view_mut() {
656 return view_mut<T>(0, m_header.count);
657 }
658
659 template <typename T>
660 GAIA_NODISCARD decltype(auto) view_mut_raw(void* ptr, uint32_t size) const {
661 using U = typename actual_type_t<T>::Type;
662 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
663
664 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
665 }
666
674 template <typename T>
675 GAIA_NODISCARD decltype(auto) sview_mut(uint16_t from, uint16_t to) {
676 using U = typename actual_type_t<T>::Type;
677 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
678
679 // Always consider full range for SoA
680 if constexpr (mem::is_soa_layout_v<U>)
681 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(0, capacity())};
682 else
683 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(from, to)};
684 }
685
686 template <typename T>
687 GAIA_NODISCARD decltype(auto) sview_mut_raw(void* ptr, uint32_t size) const {
688 using U = typename actual_type_t<T>::Type;
689 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
690
691 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
692 }
693
694 template <typename T>
695 GAIA_NODISCARD decltype(auto) sview_mut() {
696 return sview_mut<T>(0, m_header.count);
697 }
698
702 template <
703 typename T
704#if GAIA_ENABLE_HOOKS
705 ,
706 bool TriggerSetHooks
707#endif
708 >
709 GAIA_FORCEINLINE void modify() {
710 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "mod can't be used to modify Entity");
711
712 if constexpr (is_pair<T>::value) {
713 using TT = typename T::type;
714 using U = typename component_type_t<TT>::Type;
715 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
716
717#if GAIA_ASSERT_ENABLED
718 // constexpr auto kind = entity_kind_v<TT>;
719#endif
720 const auto rel = m_header.cc->get<typename T::rel>().entity;
721 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
722 const auto compIdx = comp_idx((Entity)Pair(rel, tgt));
723
724 // Update version number if necessary so we know RW access was used on the chunk
725 update_world_version(compIdx);
726
727#if GAIA_ENABLE_SET_HOOKS
728 if constexpr (TriggerSetHooks) {
729 const auto& rec = m_records.pRecords[compIdx];
730 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
731 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
732 }
733#endif
734 } else {
735 using U = typename component_type_t<T>::Type;
736 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
737
738#if GAIA_ASSERT_ENABLED
739 constexpr auto kind = entity_kind_v<T>;
740#endif
741 const auto comp = m_header.cc->get<T>().entity;
742 GAIA_ASSERT(comp.kind() == kind);
743 const auto compIdx = comp_idx(comp);
744
745 // Update version number if necessary so we know RW access was used on the chunk
746 update_world_version(compIdx);
747
748#if GAIA_ENABLE_SET_HOOKS
749 if constexpr (TriggerSetHooks) {
750 const auto& rec = m_records.pRecords[compIdx];
751 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
752 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
753 }
754#endif
755 }
756 }
757
765 template <typename T>
766 GAIA_NODISCARD decltype(auto) view_auto(uint16_t from, uint16_t to) {
767 using UOriginal = typename actual_type_t<T>::TypeOriginal;
768 if constexpr (core::is_mut_v<UOriginal>)
769 return view_mut<T>(from, to);
770 else
771 return view<T>(from, to);
772 }
773
774 template <typename T>
775 GAIA_NODISCARD decltype(auto) view_auto() {
776 return view_auto<T>(0, m_header.count);
777 }
778
787 template <typename T>
788 GAIA_NODISCARD decltype(auto) sview_auto(uint16_t from, uint16_t to) {
789 using UOriginal = typename actual_type_t<T>::TypeOriginal;
790 if constexpr (core::is_mut_v<UOriginal>)
791 return sview_mut<T>(from, to);
792 else
793 return view<T>(from, to);
794 }
795
796 template <typename T>
797 GAIA_NODISCARD decltype(auto) sview_auto() {
798 return sview_auto<T>(0, m_header.count);
799 }
800
801 GAIA_NODISCARD EntitySpan entity_view() const {
802 return {(const Entity*)m_records.pEntities, m_header.count};
803 }
804
805 GAIA_NODISCARD World& world() {
806 return *const_cast<World*>(m_header.world);
807 }
808
809 GAIA_NODISCARD const World& world() const {
810 return *m_header.world;
811 }
812
813 GAIA_NODISCARD EntitySpan ids_view() const {
814 return {(const Entity*)m_records.pCompEntities, m_header.cntEntities};
815 }
816
817 GAIA_NODISCARD std::span<const ComponentRecord> comp_rec_view() const {
818 return {m_records.pRecords, m_header.cntEntities};
819 }
820
821 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx) {
822 const auto& rec = m_records.pRecords[compIdx];
823 return rec.pData;
824 }
825
826 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx, uint32_t offset) {
827 const auto& rec = m_records.pRecords[compIdx];
828 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
829 }
830
831 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx) const {
832 const auto& rec = m_records.pRecords[compIdx];
833 return rec.pData;
834 }
835
836 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx, uint32_t offset) const {
837 const auto& rec = m_records.pRecords[compIdx];
838 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
839 }
840
843 GAIA_NODISCARD uint16_t add_entity(Entity entity) {
844 const auto row = m_header.count++;
845
846 // Zero after increase of value means an overflow!
847 GAIA_ASSERT(m_header.count != 0);
848
849 ++m_header.countEnabled;
850 entity_view_mut()[row] = entity;
851
852 return row;
853 }
854
859 static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers& recs) {
860 GAIA_PROF_SCOPE(Chunk::copy_entity_data);
861
862 auto& srcEntityContainer = recs[srcEntity];
863 auto* pSrcChunk = srcEntityContainer.pChunk;
864
865 auto& dstEntityContainer = recs[dstEntity];
866 auto* pDstChunk = dstEntityContainer.pChunk;
867
868 GAIA_ASSERT(srcEntityContainer.pArchetype == dstEntityContainer.pArchetype);
869
870 auto srcRecs = pSrcChunk->comp_rec_view();
871
872 // Copy generic component data from reference entity to our new entity.
873 // Unique components do not change place in the chunk so there is no need to move them.
874 GAIA_FOR(pSrcChunk->m_header.genEntities) {
875 const auto& rec = srcRecs[i];
876 if (!component_has_inline_data(rec.comp))
877 continue;
878
879 const auto* pSrc = (const void*)pSrcChunk->comp_ptr_mut(i);
880 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
881 rec.pItem->copy(
882 pDst, pSrc, dstEntityContainer.row, srcEntityContainer.row, pDstChunk->capacity(), pSrcChunk->capacity());
883 }
884 }
885
893 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
894 GAIA_PROF_SCOPE(Chunk::copy_entity_data_n_same_chunk);
895
896 GAIA_ASSERT(pSrcChunk != nullptr);
897 GAIA_ASSERT(pDstChunk != nullptr);
898 GAIA_ASSERT(srcRow < pSrcChunk->size());
899 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
900 GAIA_ASSERT(pSrcChunk->ids_view().size() == pDstChunk->ids_view().size());
901
902 auto srcRecs = pSrcChunk->comp_rec_view();
903
904 // Copy generic component data from the reference entity to all newly allocated rows.
905 // Unique components do not change place in the chunk so there is no need to move them.
906 GAIA_FOR(pSrcChunk->m_header.genEntities) {
907 const auto& rec = srcRecs[i];
908 if (!component_has_inline_data(rec.comp))
909 continue;
910
911 const auto* pSrc = (const void*)pSrcChunk->comp_ptr(i);
912 GAIA_FOR_(dstCount, rowOffset) {
913 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
914 rec.pItem->copy(pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
915 }
916 }
917 }
918
926 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
927 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data_n);
928
929 GAIA_ASSERT(pSrcChunk != nullptr);
930 GAIA_ASSERT(pDstChunk != nullptr);
931 GAIA_ASSERT(srcRow < pSrcChunk->size());
932 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
933
934 auto srcIds = pSrcChunk->ids_view();
935 auto dstIds = pDstChunk->ids_view();
936 auto dstRecs = pDstChunk->comp_rec_view();
937
938 uint32_t i = 0;
939 uint32_t j = 0;
940 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
941 const auto oldId = srcIds[i];
942 const auto newId = dstIds[j];
943
944 if (oldId == newId) {
945 const auto& rec = dstRecs[j];
946 if (component_has_inline_data(rec.comp)) {
947 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
948 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
949 GAIA_FOR_(dstCount, rowOffset) {
950 rec.pItem->ctor_copy(
951 pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
952 }
953 }
954
955 ++i;
956 ++j;
957 } else if (SortComponentCond{}.operator()(oldId, newId)) {
958 ++i;
959 } else {
960 const auto& rec = dstRecs[j];
961 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
962 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
963 rec.pItem->func_ctor(pDst, dstCount);
964 }
965
966 ++j;
967 }
968 }
969
970 for (; j < pDstChunk->m_header.genEntities; ++j) {
971 const auto& rec = dstRecs[j];
972 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
973 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
974 rec.pItem->func_ctor(pDst, dstCount);
975 }
976 }
977 }
978
983 void move_entity_data(Entity entity, uint16_t row, EntityContainers& recs) {
984 GAIA_PROF_SCOPE(Chunk::move_entity_data);
985
986 auto& ec = recs[entity];
987 auto* pSrcChunk = ec.pChunk;
988 auto srcRecs = pSrcChunk->comp_rec_view();
989
990 // Copy generic component data from reference entity to our new entity.
991 // Unique components do not change place in the chunk so there is no need to move them.
992 GAIA_FOR(pSrcChunk->m_header.genEntities) {
993 const auto& rec = srcRecs[i];
994 if (!component_has_inline_data(rec.comp))
995 continue;
996
997 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
998 auto* pDst = (void*)comp_ptr_mut(i);
999 rec.pItem->ctor_move(pDst, pSrc, row, ec.row, capacity(), pSrcChunk->capacity());
1000 }
1001 }
1002
1008 static void copy_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1009 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data);
1010
1011 GAIA_ASSERT(pSrcChunk != nullptr);
1012 GAIA_ASSERT(pDstChunk != nullptr);
1013 GAIA_ASSERT(srcRow < pSrcChunk->size());
1014 GAIA_ASSERT(dstRow < pDstChunk->size());
1015
1016 auto srcIds = pSrcChunk->ids_view();
1017 auto dstIds = pDstChunk->ids_view();
1018 auto dstRecs = pDstChunk->comp_rec_view();
1019
1020 // Find intersection of the two component lists.
1021 // Arrays are sorted so we can do linear intersection lookup.
1022 // Call constructor on each match.
1023 // Unique components do not change place in the chunk so there is no need to move them.
1024 {
1025 uint32_t i = 0;
1026 uint32_t j = 0;
1027 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1028 const auto oldId = srcIds[i];
1029 const auto newId = dstIds[j];
1030
1031 if (oldId == newId) {
1032 const auto& rec = dstRecs[j];
1033 if (component_has_inline_data(rec.comp)) {
1034 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1035 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1036 rec.pItem->ctor_copy(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1037 }
1038
1039 ++i;
1040 ++j;
1041 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1042 ++i;
1043 } else {
1044 // No match with the old chunk. Construct the component
1045 const auto& rec = dstRecs[j];
1046 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1047 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1048 rec.pItem->func_ctor(pDst, 1);
1049 }
1050
1051 ++j;
1052 }
1053 }
1054
1055 // Initialize the rest of the components if they are generic.
1056 for (; j < pDstChunk->m_header.genEntities; ++j) {
1057 const auto& rec = dstRecs[j];
1058 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1059 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1060 rec.pItem->func_ctor(pDst, 1);
1061 }
1062 }
1063 }
1064 }
1065
1071 static void move_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1072 GAIA_PROF_SCOPE(Chunk::move_foreign_entity_data);
1073
1074 GAIA_ASSERT(pSrcChunk != nullptr);
1075 GAIA_ASSERT(pDstChunk != nullptr);
1076 GAIA_ASSERT(srcRow < pSrcChunk->size());
1077 GAIA_ASSERT(dstRow < pDstChunk->size());
1078
1079 auto srcIds = pSrcChunk->ids_view();
1080 auto dstIds = pDstChunk->ids_view();
1081 auto dstRecs = pDstChunk->comp_rec_view();
1082
1083 // Find intersection of the two component lists.
1084 // Arrays are sorted so we can do linear intersection lookup.
1085 // Call constructor on each match.
1086 // Unique components do not change place in the chunk so there is no need to move them.
1087 {
1088 uint32_t i = 0;
1089 uint32_t j = 0;
1090 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1091 const auto oldId = srcIds[i];
1092 const auto newId = dstIds[j];
1093
1094 if (oldId == newId) {
1095 const auto& rec = dstRecs[j];
1096 if (component_has_inline_data(rec.comp)) {
1097 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1098 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1099 rec.pItem->ctor_move(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1100 }
1101
1102 ++i;
1103 ++j;
1104 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1105 ++i;
1106 } else {
1107 // No match with the old chunk. Construct the component
1108 const auto& rec = dstRecs[j];
1109 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1110 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1111 rec.pItem->func_ctor(pDst, 1);
1112 }
1113
1114 ++j;
1115 }
1116 }
1117
1118 // Initialize the rest of the components if they are generic.
1119 for (; j < pDstChunk->m_header.genEntities; ++j) {
1120 const auto& rec = dstRecs[j];
1121 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1122 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1123 rec.pItem->func_ctor(pDst, 1);
1124 }
1125 }
1126 }
1127 }
1128
1135 void remove_entity_inter(uint16_t row, EntityContainers& recs) {
1136 GAIA_PROF_SCOPE(Chunk::remove_entity_inter);
1137
1138 const uint16_t rowA = row;
1139 const uint16_t rowB = m_header.count - 1;
1140 // The "rowA" entity is the one we are going to destroy so it needs to precede the "rowB"
1141 GAIA_ASSERT(rowA <= rowB);
1142
1143 // To move anything, we need at least 2 entities
1144 if GAIA_LIKELY (rowA < rowB) {
1145 GAIA_ASSERT(m_header.count > 1);
1146
1147 auto ev = entity_view_mut();
1148
1149 // Update entity data
1150 const auto entityB = ev[rowB];
1151 auto& ecB = recs[entityB];
1152#if GAIA_ASSERT_ENABLED
1153 const auto entityA = ev[rowA];
1154 auto& ecA = recs[entityA];
1155
1156 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1157 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1158#endif
1159
1160 ev[rowA] = entityB;
1161
1162 // Move component data from entityB to entityA
1163 auto recView = comp_rec_view();
1164 GAIA_FOR(m_header.genEntities) {
1165 const auto& rec = recView[i];
1166 if (!component_has_inline_data(rec.comp))
1167 continue;
1168
1169 auto* pSrc = (void*)comp_ptr_mut(i);
1170 rec.pItem->move(pSrc, pSrc, rowA, rowB, capacity(), capacity());
1171
1172 pSrc = (void*)comp_ptr_mut(i, rowB);
1173 rec.pItem->dtor(pSrc);
1174 }
1175
1176 // Entity has been replaced with the last one in our chunk. Update its container record.
1177 ecB.row = rowA;
1178 ecB.pEntity = &ev[rowA];
1179 } else if (m_header.hasAnyCustomGenDtor) {
1180 // This is the last entity in the chunk so simply destroy its data
1181 auto recView = comp_rec_view();
1182 GAIA_FOR(m_header.genEntities) {
1183 const auto& rec = recView[i];
1184 if (!component_has_inline_data(rec.comp))
1185 continue;
1186
1187 auto* pSrc = (void*)comp_ptr_mut(i, rowA);
1188 rec.pItem->dtor(pSrc);
1189 }
1190 }
1191 }
1192
1199 void remove_entity(uint16_t row, EntityContainers& recs) {
1200 if GAIA_UNLIKELY (m_header.count == 0)
1201 return;
1202
1203 GAIA_PROF_SCOPE(Chunk::remove_entity);
1204
1205 if (enabled(row)) {
1206 // Entity was previously enabled. Swap with the last entity
1207 remove_entity_inter(row, recs);
1208 // If this was the first enabled entity make sure to update the row
1209 if (m_header.rowFirstEnabledEntity > 0 && row == m_header.rowFirstEnabledEntity)
1210 --m_header.rowFirstEnabledEntity;
1211 // At this point the last entity is no longer valid so remove it
1212 remove_last_entity();
1213 --m_header.countEnabled;
1214 } else {
1215 // Entity was previously disabled. Swap with the last disabled entity
1216 const uint16_t pivot = size_disabled() - 1;
1217 swap_chunk_entities(row, pivot, recs);
1218 // Once swapped, try to swap with the last (enabled) entity in the chunk.
1219 remove_entity_inter(pivot, recs);
1220 --m_header.rowFirstEnabledEntity;
1221 // At this point the last entity is no longer valid so remove it
1222 remove_last_entity();
1223 }
1224 }
1225
1233 void swap_chunk_entities(uint16_t rowA, uint16_t rowB, EntityContainers& recs) {
1234 // If there are at least two different entities inside to swap
1235 if GAIA_UNLIKELY (m_header.count <= 1 || rowA == rowB)
1236 return;
1237
1238 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1239
1240 // Update entity data
1241 auto ev = entity_view_mut();
1242 const auto entityA = ev[rowA];
1243 const auto entityB = ev[rowB];
1244
1245 auto& ecA = recs[entityA];
1246 auto& ecB = recs[entityB];
1247 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1248 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1249
1250 ev[rowA] = entityB;
1251 ev[rowB] = entityA;
1252
1253 // Swap component data
1254 auto recView = comp_rec_view();
1255 GAIA_FOR(m_header.genEntities) {
1256 const auto& rec = recView[i];
1257 if (!component_has_inline_data(rec.comp))
1258 continue;
1259
1260 GAIA_ASSERT(rec.pData == comp_ptr_mut(i));
1261 rec.pItem->swap(rec.pData, rec.pData, rowA, rowB, capacity(), capacity());
1262 }
1263
1264 // Update indices in entity container.
1265 ecA.row = rowB;
1266 ecB.row = rowA;
1267 ecA.pEntity = &ev[rowB];
1268 ecB.pEntity = &ev[rowA];
1269 }
1270
1277 static void swap_chunk_entities(World& world, Entity entityA, Entity entityB) {
1278 // Don't swap if the two entities are the same
1279 if GAIA_UNLIKELY (entityA == entityB)
1280 return;
1281
1282 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1283
1284 auto& ecA = fetch_mut(world, entityA);
1285 auto& ecB = fetch_mut(world, entityB);
1286
1287 // Make sure the two entities are in the same archetype
1288 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1289 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1290
1291 auto* pChunkA = ecA.pChunk;
1292 auto* pChunkB = ecB.pChunk;
1293
1294 // Swap entities in the entity data part
1295 pChunkA->entity_view_mut()[ecA.row] = entityB;
1296 pChunkB->entity_view_mut()[ecB.row] = entityA;
1297
1298 // Swap component data
1299 auto recViewA = pChunkA->comp_rec_view();
1300 GAIA_FOR(pChunkA->m_header.genEntities) {
1301 const auto& recA = recViewA[i];
1302 if (!component_has_inline_data(recA.comp))
1303 continue;
1304
1305 auto* pDataA = pChunkA->comp_rec_view()[i].pData;
1306 auto* pDataB = pChunkB->comp_rec_view()[i].pData;
1307 recA.pItem->swap(
1308 // Data pointers
1309 pDataA, pDataB,
1310 // Rows
1311 ecA.row, ecB.row,
1312 // Chunk capacities
1313 pChunkA->capacity(), pChunkA->capacity() //
1314 );
1315 }
1316
1317 // Update indices and chunks in entity container.
1318 core::swap(ecA.row, ecB.row);
1319 core::swap(ecA.pChunk, ecB.pChunk);
1320 }
1321
1326 void enable_entity(uint16_t row, bool enableEntity, EntityContainers& recs) {
1327 GAIA_ASSERT(row < m_header.count && "Entity chunk row out of bounds!");
1328
1329 if (enableEntity) {
1330 // Nothing to enable if there are no disabled entities
1331 if (!m_header.has_disabled_entities())
1332 return;
1333 // Trying to enable an already enabled entity
1334 if (enabled(row))
1335 return;
1336 // Try swapping our entity with the last disabled one
1337 const auto entity = entity_view()[row];
1338 swap_chunk_entities(--m_header.rowFirstEnabledEntity, row, recs);
1339 recs[entity].data.dis = 0;
1340 ++m_header.countEnabled;
1341 } else {
1342 // Nothing to disable if there are no enabled entities
1343 if (!m_header.has_enabled_entities())
1344 return;
1345 // Trying to disable an already disabled entity
1346 if (!enabled(row))
1347 return;
1348 // Try swapping our entity with the last one in our chunk
1349 const auto entity = entity_view()[row];
1350 swap_chunk_entities(m_header.rowFirstEnabledEntity++, row, recs);
1351 recs[entity].data.dis = 1;
1352 --m_header.countEnabled;
1353 }
1354 }
1355
1359 bool enabled(uint16_t row) const {
1360 GAIA_ASSERT(m_header.count > 0);
1361
1362 return row >= (uint16_t)m_header.rowFirstEnabledEntity;
1363 }
1364
1368 uint8_t& data(uint32_t offset) {
1369 return m_data[offset];
1370 }
1371
1375 const uint8_t& data(uint32_t offset) const {
1376 return m_data[offset];
1377 }
1378
1379 //----------------------------------------------------------------------
1380 // Component handling
1381 //----------------------------------------------------------------------
1382
1383 void call_ctor(uint32_t entIdx, const ComponentCacheItem& item) {
1384 if (item.func_ctor == nullptr || !component_has_inline_data(item.comp))
1385 return;
1386
1387 GAIA_PROF_SCOPE(Chunk::call_ctor);
1388
1389 const auto compIdx = comp_idx(item.entity);
1390 auto* pSrc = (void*)comp_ptr_mut(compIdx, entIdx);
1391 item.func_ctor(pSrc, 1);
1392 }
1393
1394 void call_gen_ctors(uint32_t entIdx, uint32_t entCnt) {
1395 if (!m_header.hasAnyCustomGenCtor)
1396 return;
1397
1398 GAIA_PROF_SCOPE(Chunk::call_gen_ctors);
1399
1400 auto recs = comp_rec_view();
1401 GAIA_FOR(m_header.genEntities) {
1402 const auto& rec = recs[i];
1403 if (!component_has_inline_data(rec.comp))
1404 continue;
1405
1406 const auto* pItem = rec.pItem;
1407 if (pItem == nullptr || pItem->func_ctor == nullptr)
1408 continue;
1409
1410 auto* pSrc = (void*)comp_ptr_mut(i, entIdx);
1411 pItem->func_ctor(pSrc, entCnt);
1412 }
1413 }
1414
1415 void call_all_dtors() {
1416 if (!m_header.hasAnyCustomGenDtor && !m_header.hasAnyCustomUniCtor)
1417 return;
1418
1419 GAIA_PROF_SCOPE(Chunk::call_all_dtors);
1420
1421 auto ids = ids_view();
1422 auto recs = comp_rec_view();
1423 const auto recs_cnt = recs.size();
1424 GAIA_FOR(recs_cnt) {
1425 const auto& rec = recs[i];
1426 if (!component_has_inline_data(rec.comp))
1427 continue;
1428
1429 const auto* pItem = rec.pItem;
1430 if (pItem == nullptr || pItem->func_dtor == nullptr)
1431 continue;
1432
1433 auto* pSrc = (void*)comp_ptr_mut(i, 0);
1434 const auto e = ids[i];
1435 const auto cnt = (e.kind() == EntityKind::EK_Gen) ? m_header.count : (uint16_t)1;
1436 pItem->func_dtor(pSrc, cnt);
1437 }
1438 };
1439
1440 //----------------------------------------------------------------------
1441 // Check component presence
1442 //----------------------------------------------------------------------
1443
1447 GAIA_NODISCARD bool has(Entity entity) const {
1448 auto ids = ids_view();
1449 return core::has(ids, entity);
1450 }
1451
1455 template <typename T>
1456 GAIA_NODISCARD bool has() const {
1457 if constexpr (is_pair<T>::value) {
1458 const auto rel = m_header.cc->get<typename T::rel>().entity;
1459 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1460 return has((Entity)Pair(rel, tgt));
1461 } else {
1462 const auto* pComp = m_header.cc->find<T>();
1463 return pComp != nullptr && has(pComp->entity);
1464 }
1465 }
1466
1467 //----------------------------------------------------------------------
1468 // Set component data
1469 //----------------------------------------------------------------------
1470
1475 template <typename T>
1476 decltype(auto) set(uint16_t row) {
1477 verify_comp<T>();
1478
1479 GAIA_ASSERT2(
1480 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1481 "Set providing a row can only be used with generic components");
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
1495 template <typename T>
1496 decltype(auto) set_idx(uint16_t row, uint32_t compIdx) {
1497 verify_comp<T>();
1498
1499 GAIA_ASSERT2(
1500 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1501 "Set providing a row can only be used with generic components");
1502
1503 // Update the world version
1504 ::gaia::ecs::update_version(m_header.worldVersion);
1505
1506 world_notify_on_set(
1507 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, row, (uint16_t)(row + 1));
1508 return comp_mut_idx<T, true>(row, compIdx);
1509 }
1510
1514 template <typename T>
1515 decltype(auto) set_idx(uint32_t compIdx) {
1516 verify_comp<T>();
1517 static_assert(
1518 entity_kind_v<T> != EntityKind::EK_Gen,
1519 "Set not providing a row can only be used with non-generic components");
1520
1521 // Update the world version
1522 ::gaia::ecs::update_version(m_header.worldVersion);
1523
1524 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, 0, 1);
1525 return comp_mut_idx<T, true>(0, compIdx);
1526 }
1527
1532 template <typename T>
1533 decltype(auto) set(uint16_t row, Entity type) {
1534 GAIA_ASSERT2(
1535 type.kind() == EntityKind::EK_Gen || row == 0,
1536 "Set providing a row can only be used with generic components");
1537 GAIA_ASSERT(type.kind() == entity_kind_v<T>);
1538 const uint32_t compIdx = comp_idx(type);
1539
1540 // Update the world version
1541 ::gaia::ecs::update_version(m_header.worldVersion);
1542
1543 GAIA_ASSERT(row < m_header.capacity);
1544 world_notify_on_set(*const_cast<World*>(m_header.world), type, *this, row, (uint16_t)(row + 1));
1545 return comp_mut_idx<T, true>(row, compIdx);
1546 }
1547
1553 template <typename T>
1554 decltype(auto) sset(uint16_t row) {
1555 GAIA_ASSERT2(
1556 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1557 "Set providing a row can only be used with generic components");
1558
1559 GAIA_ASSERT(row < m_header.capacity);
1560 return sview_mut<T>()[row];
1561 }
1562
1565 template <typename T>
1566 decltype(auto) sset_idx(uint16_t row, uint32_t compIdx) {
1567 verify_comp<T>();
1568
1569 GAIA_ASSERT2(
1570 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1571 "Set providing a row can only be used with generic components");
1572
1573 return comp_mut_idx<T, false>(row, compIdx);
1574 }
1575
1578 template <typename T>
1579 decltype(auto) sset_idx(uint32_t compIdx) {
1580 verify_comp<T>();
1581 static_assert(
1582 entity_kind_v<T> != EntityKind::EK_Gen,
1583 "SSet not providing a row can only be used with non-generic components");
1584
1585 return comp_mut_idx<T, false>(0, compIdx);
1586 }
1587
1594 template <typename T>
1595 decltype(auto) sset(uint16_t row, Entity type) {
1596 static_assert(core::is_raw_v<T>);
1597
1598 GAIA_ASSERT2(
1599 type.kind() == EntityKind::EK_Gen || row == 0,
1600 "Set providing a row can only be used with generic components");
1601 GAIA_ASSERT(type.kind() == entity_kind_v<T>);
1602 const uint32_t compIdx = comp_idx(type);
1603
1604 GAIA_ASSERT(row < m_header.capacity);
1605 return comp_mut_idx<T, false>(row, compIdx);
1606 }
1607
1608 //----------------------------------------------------------------------
1609 // Read component data
1610 //----------------------------------------------------------------------
1611
1618 template <typename T>
1619 GAIA_NODISCARD decltype(auto) get(uint16_t row) const {
1620 static_assert(
1621 entity_kind_v<T> == EntityKind::EK_Gen, "Get providing a row can only be used with generic components");
1622
1623 return comp_inter<T>(row);
1624 }
1625
1630 template <typename T>
1631 GAIA_NODISCARD decltype(auto) get_idx(uint16_t row, uint32_t compIdx) const {
1632 static_assert(
1633 entity_kind_v<T> == EntityKind::EK_Gen, "Get providing a row can only be used with generic components");
1634
1635 return comp_inter_idx<T>(row, compIdx);
1636 }
1637
1643 template <typename T>
1644 GAIA_NODISCARD decltype(auto) get(uint16_t row, Entity type) const {
1645 GAIA_ASSERT2(
1646 type.kind() == EntityKind::EK_Gen || row == 0,
1647 "Get providing a row can only be used with generic components");
1648 GAIA_ASSERT(type.kind() == entity_kind_v<T>);
1649 GAIA_ASSERT(row < m_header.count);
1650 const uint32_t compIdx = comp_idx(type);
1651 return comp_inter_idx<T>(row, compIdx);
1652 }
1653
1658 template <typename T>
1659 GAIA_NODISCARD decltype(auto) get() const {
1660 static_assert(
1661 entity_kind_v<T> != EntityKind::EK_Gen,
1662 "Get not providing a row can only be used with non-generic components");
1663
1664 return comp_inter<T>(0);
1665 }
1666
1670 template <typename T>
1671 GAIA_NODISCARD decltype(auto) get_idx(uint32_t compIdx) const {
1672 static_assert(
1673 entity_kind_v<T> != EntityKind::EK_Gen,
1674 "Get not providing a row can only be used with non-generic components");
1675
1676 return comp_inter_idx<T>(0, compIdx);
1677 }
1678
1679 template <typename T>
1680 GAIA_NODISCARD Entity comp_entity() const {
1681 if constexpr (is_pair<T>::value) {
1682 const auto rel = m_header.cc->get<typename T::rel>().entity;
1683 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1684 return (Entity)Pair(rel, tgt);
1685 } else {
1686 return m_header.cc->get<T>().entity;
1687 }
1688 }
1689
1694 GAIA_NODISCARD uint32_t comp_idx(Entity entity) const {
1695 return ecs::comp_idx<ChunkHeader::MAX_COMPONENTS>(m_records.pCompEntities, entity);
1696 }
1697
1703 GAIA_NODISCARD uint32_t comp_idx(Entity entity, uint32_t offset) const {
1704 return ecs::comp_idx({m_records.pCompEntities + offset, m_header.count - offset}, entity);
1705 }
1706
1707 //----------------------------------------------------------------------
1708
1710 void set_idx(uint32_t value) {
1711 m_header.index = value;
1712 }
1713
1715 GAIA_NODISCARD uint32_t idx() const {
1716 return m_header.index;
1717 }
1718
1720 GAIA_NODISCARD bool has_enabled_entities() const {
1721 return m_header.has_enabled_entities();
1722 }
1723
1725 GAIA_NODISCARD bool has_disabled_entities() const {
1726 return m_header.has_disabled_entities();
1727 }
1728
1730 GAIA_NODISCARD bool dying() const {
1731 return m_header.lifespanCountdown > 0;
1732 }
1733
1735 GAIA_NODISCARD bool queued_for_deletion() const {
1736 return m_header.deleteQueueIndex != BadIndex;
1737 }
1738
1740 GAIA_NODISCARD uint32_t delete_queue_index() const {
1741 return m_header.deleteQueueIndex;
1742 }
1743
1745 void delete_queue_index(uint32_t idx) {
1746 m_header.deleteQueueIndex = idx;
1747 }
1748
1751 m_header.deleteQueueIndex = BadIndex;
1752 }
1753
1755 void die() {
1756 m_header.dead = 1;
1757 }
1758
1760 GAIA_NODISCARD bool dead() const {
1761 return m_header.dead == 1;
1762 }
1763
1766 GAIA_ASSERT(!dead());
1767 GAIA_ASSERT(!queued_for_deletion());
1768 m_header.lifespanCountdown = ChunkHeader::MAX_CHUNK_LIFESPAN;
1769 }
1770
1772 void revive() {
1773 GAIA_ASSERT(!dead());
1774 m_header.lifespanCountdown = 0;
1775 clear_delete_queue_index();
1776 }
1777
1781 GAIA_ASSERT(dying());
1782 --m_header.lifespanCountdown;
1783 return dying();
1784 }
1785
1787 GAIA_NODISCARD bool full() const {
1788 return m_header.count >= m_header.capacity;
1789 }
1790
1792 GAIA_NODISCARD bool is_semi() const {
1793 // We want the chunk filled to at least 75% before considering it semi-full
1794 constexpr float Threshold = 0.75f;
1795 return ((float)m_header.count / (float)m_header.capacity) < Threshold;
1796 }
1797
1799 GAIA_NODISCARD uint16_t size() const {
1800 return m_header.count;
1801 }
1802
1804 GAIA_NODISCARD bool empty() const {
1805 return m_header.count == 0;
1806 }
1807
1809 GAIA_NODISCARD uint16_t size_enabled() const {
1810 return m_header.countEnabled;
1811 }
1812
1814 GAIA_NODISCARD uint16_t size_disabled() const {
1815 return (uint16_t)m_header.rowFirstEnabledEntity;
1816 }
1817
1819 GAIA_NODISCARD uint16_t capacity() const {
1820 return m_header.capacity;
1821 }
1822
1824 GAIA_NODISCARD uint8_t size_generic() const {
1825 return m_header.genEntities;
1826 }
1827
1831 GAIA_NODISCARD bool changed(uint32_t requiredVersion) const {
1832 const auto* versions = m_records.pVersions;
1833 const auto changeVersion = versions[0];
1834 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1835 }
1836
1838 GAIA_NODISCARD bool changed(uint32_t requiredVersion, uint32_t compIdx) const {
1839 const auto* versions = m_records.pVersions;
1840 // Do +1 because index 0 is reserved for the entity version number.
1841 const auto changeVersion = versions[compIdx + 1];
1842 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1843 }
1844
1847 GAIA_NODISCARD bool entity_order_changed(uint32_t requiredVersion) const {
1848 return ::gaia::ecs::version_changed(m_header.entityOrderVersion, requiredVersion);
1849 }
1850
1852 GAIA_FORCEINLINE void update_world_version(uint32_t compIdx) {
1853 auto versions = comp_version_view_mut();
1854 // Automatically treat the entity as changed.
1855 versions[0] = m_header.worldVersion;
1856 // Do +1 because index 0 is reserved for the entity version number.
1857 versions[compIdx + 1] = m_header.worldVersion;
1858 // Sorted queries keyed by this component can invalidate their cached order immediately.
1859 world_invalidate_sorted_queries_for_entity(
1860 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx]);
1861 }
1862
1864 GAIA_FORCEINLINE void update_entity_order_version() {
1865 m_header.entityOrderVersion = m_header.worldVersion;
1866 // Row-order changes invalidate cached sorted slices regardless of sort key.
1867 world_invalidate_sorted_queries(*const_cast<World*>(m_header.world));
1868 }
1869
1871 GAIA_FORCEINLINE void update_world_version() {
1872 // Edit the version pointer directly. The first elements is always the entity version.
1873 // This area of memory is always present.
1874 auto* versions = m_records.pVersions;
1875 // We update the version of the entity only. If this one changes,
1876 // all other components are considered changed as well.
1877 versions[0] = m_header.worldVersion;
1878 }
1879
1881 GAIA_FORCEINLINE void update_world_version_init() {
1882 auto* versions = m_records.pVersions;
1883 // We update the version of the entity and all components to match the world version.
1884 versions[0] = m_header.worldVersion;
1885 GAIA_FOR(m_header.genEntities) versions[1 + i] = m_header.worldVersion;
1886 m_header.entityOrderVersion = m_header.worldVersion;
1887 }
1888
1889 void diag() const {
1890 GAIA_LOG_N(
1891 " Chunk #%04u, entities:%u/%u, lifespanCountdown:%u", m_header.index, m_header.count, m_header.capacity,
1892 m_header.lifespanCountdown);
1893 }
1894 };
1895 } // namespace ecs
1896} // namespace gaia
Array with variable size of elements of type.
Definition darray_impl.h:25
Definition span_impl.h:99
Definition chunk.h:35
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:788
GAIA_NODISCARD uint16_t capacity() const
Returns the number of entities in the chunk.
Definition chunk.h:1819
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:1671
static void free(Chunk *pChunk)
Releases all memory allocated by pChunk.
Definition chunk.h:496
const uint8_t & data(uint32_t offset) const
Returns an immutable pointer to chunk data.
Definition chunk.h:1375
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:1496
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:1595
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:1199
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:892
GAIA_FORCEINLINE void update_world_version()
Update the version of all components.
Definition chunk.h:1871
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:1703
bool progress_death()
Updates internal lifespan.
Definition chunk.h:1780
decltype(auto) set(uint16_t row)
Sets the value of the unique component T on row in the chunk.
Definition chunk.h:1476
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:339
void die()
Marks the chunk as dead (ready to delete)
Definition chunk.h:1755
GAIA_NODISCARD uint32_t idx() const
Returns the index of this chunk in its archetype's storage.
Definition chunk.h:1715
uint8_t & data(uint32_t offset)
Returns a mutable pointer to chunk data.
Definition chunk.h:1368
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:1566
void update_versions()
Updates the version numbers for this chunk.
Definition chunk.h:602
GAIA_FORCEINLINE void update_world_version(uint32_t compIdx)
Update the version of a component at the index.
Definition chunk.h:1852
GAIA_NODISCARD bool has_disabled_entities() const
Checks is this chunk has any disabled entities.
Definition chunk.h:1725
GAIA_NODISCARD bool full() const
Checks is the full capacity of the has has been reached.
Definition chunk.h:1787
GAIA_NODISCARD bool is_semi() const
Checks is the chunk is semi-full.
Definition chunk.h:1792
GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const
Returns a read-only entity or component view.
Definition chunk.h:615
GAIA_NODISCARD uint16_t add_entity(Entity entity)
Make.
Definition chunk.h:843
GAIA_NODISCARD uint16_t size_enabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1809
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:766
void start_dying()
Starts the process of dying (not yet ready to delete, can be revived)
Definition chunk.h:1765
GAIA_NODISCARD bool has() const
Checks if component T is present in the chunk.
Definition chunk.h:1456
GAIA_NODISCARD uint16_t size() const
Returns the total number of entities in the chunk (both enabled and disabled)
Definition chunk.h:1799
GAIA_FORCEINLINE void update_entity_order_version()
Updates the entity-order version after rows were added, removed, or reordered.
Definition chunk.h:1864
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:1619
GAIA_NODISCARD uint32_t delete_queue_index() const
Returns the index inside World's deferred chunk-delete queue.
Definition chunk.h:1740
static uintptr_t chunk_data_area_offset()
Returns the relative offset of m_data in Chunk.
Definition chunk.h:454
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:983
decltype(auto) set_idx(uint32_t compIdx)
Sets the value of a unique component using a pre-resolved component column.
Definition chunk.h:1515
void set_idx(uint32_t value)
Sets the index of this chunk in its archetype's storage.
Definition chunk.h:1710
void clear_delete_queue_index()
Clears the deferred chunk-delete queue index.
Definition chunk.h:1750
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:1847
GAIA_NODISCARD decltype(auto) get() const
Returns the value stored in the unique component T.
Definition chunk.h:1659
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:1838
void delete_queue_index(uint32_t idx)
Stores the index inside World's deferred chunk-delete queue.
Definition chunk.h:1745
decltype(auto) sset_idx(uint32_t compIdx)
Sets the value of a unique component using a pre-resolved component column.
Definition chunk.h:1579
static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers &recs)
Copies all data associated with srcEntity into dstEntity.
Definition chunk.h:859
bool enabled(uint16_t row) const
Checks if the entity is enabled.
Definition chunk.h:1359
GAIA_NODISCARD bool dying() const
Checks is this chunk is dying.
Definition chunk.h:1730
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:1831
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:589
GAIA_NODISCARD uint16_t size_disabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1814
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:1233
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:1071
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:1326
GAIA_NODISCARD uint32_t comp_idx(Entity entity) const
Returns the internal index of a component based on the provided entity.
Definition chunk.h:1694
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:1277
GAIA_FORCEINLINE void update_world_version_init()
Update the version of all components on chunk init.
Definition chunk.h:1881
GAIA_NODISCARD bool dead() const
Checks is this chunk is dead (ready to delete)
Definition chunk.h:1760
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:1533
GAIA_NODISCARD bool has_enabled_entities() const
Checks is this chunk has any enabled entities.
Definition chunk.h:1720
void revive()
Makes a dying chunk alive again.
Definition chunk.h:1772
GAIA_NODISCARD bool has(Entity entity) const
Checks if a component/entity entity is present in the chunk.
Definition chunk.h:1447
GAIA_NODISCARD bool queued_for_deletion() const
Returns true when the chunk is currently queued for deferred deletion.
Definition chunk.h:1735
GAIA_NODISCARD bool empty() const
Checks is there are any entities in the chunk.
Definition chunk.h:1804
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:1008
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:1135
GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to)
Returns a mutable entity or component view.
Definition chunk.h:643
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:356
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:675
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:1631
GAIA_NODISCARD uint8_t size_generic() const
Returns the total number of generic entities/components in the chunk.
Definition chunk.h:1824
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:1644
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:465
decltype(auto) sset(uint16_t row)
Sets the value of the unique component T on row in the chunk.
Definition chunk.h:1554
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:925
GAIA_FORCEINLINE void modify()
Marks the component.
Definition chunk.h:709
Cache for compile-time defined components.
Definition component_cache.h:24
GAIA_NODISCARD const ComponentCacheItem & get(Entity entity) const noexcept
Returns the component cache item.
Definition component_cache.h:408
GAIA_NODISCARD const ComponentCacheItem * find(Entity entity) const noexcept
Searches for the component cache item.
Definition component_cache.h:389
Definition world.h:174
Wrapper for two Entities forming a relationship pair.
Definition id.h:529
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
constexpr uint32_t BadIndex
Sentinel index value returned by helpers when a lookup fails.
Definition utility.h:20
Strict weak ordering functor using operator<.
Definition utility.h:1368
Definition chunk_header.h:19
ChunkDataVersionOffset firstByte_Versions
Byte at which the first version number is located.
Definition chunk_header.h:21
ChunkDataOffset firstByte_EntityData
Byte at which the first entity is located.
Definition chunk_header.h:27
ChunkDataOffset firstByte_Records
Byte at which the first component id is located.
Definition chunk_header.h:25
ChunkDataOffset firstByte_CompEntities
Byte at which the first entity id is located.
Definition chunk_header.h:23
Definition chunk_header.h:50
uint32_t & worldVersion
Version of the world (stable pointer to parent world's world version)
Definition chunk_header.h:101
const ComponentCache * cc
Component cache reference.
Definition chunk_header.h:67
uint32_t index
Chunk index in its archetype list.
Definition chunk_header.h:69
uint16_t capacity
Capacity (copied from the owner archetype).
Definition chunk_header.h:77
uint16_t hasAnyCustomGenCtor
True if there's any generic component that requires custom construction.
Definition chunk_header.h:82
uint16_t dead
True if deleted, false otherwise.
Definition chunk_header.h:92
uint16_t hasAnyCustomUniDtor
True if there's any unique component that requires custom destruction.
Definition chunk_header.h:88
uint16_t hasAnyCustomGenDtor
True if there's any generic component that requires custom destruction.
Definition chunk_header.h:86
uint8_t cntEntities
Number of components on the archetype.
Definition chunk_header.h:99
uint8_t genEntities
Number of generic entities/components.
Definition chunk_header.h:97
uint32_t deleteQueueIndex
Index in World's chunk-delete queue. BadIndex when not queued for deletion.
Definition chunk_header.h:71
uint16_t lifespanCountdown
When it hits 0 the chunk is scheduled for deletion.
Definition chunk_header.h:90
uint16_t countEnabled
Number of enabled entities in the chunk.
Definition chunk_header.h:75
const World * world
Parent world.
Definition chunk_header.h:65
uint32_t entityOrderVersion
Version tracking entity order changes inside the chunk. Updated on entity add/remove/move operations,...
Definition chunk_header.h:104
uint16_t rowFirstEnabledEntity
Index of the first enabled entity in the chunk.
Definition chunk_header.h:80
uint16_t count
Total number of entities in the chunk.
Definition chunk_header.h:73
uint16_t hasAnyCustomUniCtor
True if there's any unique component that requires custom construction.
Definition chunk_header.h:84
Definition chunk_header.h:39
ComponentRecord * pRecords
Pointer to the array of component records.
Definition chunk_header.h:45
Entity * pCompEntities
Pointer to where (component) entities are stored.
Definition chunk_header.h:43
Entity * pEntities
Pointer to the array of entities.
Definition chunk_header.h:47
ComponentVersion * pVersions
Pointer to where component versions are stored.
Definition chunk_header.h:41
Definition component_cache_item.h:26
Entity entity
Component entity.
Definition component_cache_item.h:84
Component comp
Unique component identifier.
Definition component_cache_item.h:86
FuncCtor * func_ctor
Function to call when the component needs to be constructed.
Definition component_cache_item.h:97
Definition chunk_header.h:30
uint8_t * pData
Pointer to where the first instance of the component is stored.
Definition chunk_header.h:34
Definition id.h:35
Definition entity_container.h:142
Definition id.h:247
Definition id.h:239
Definition data_layout_policy.h:90
Definition data_layout_policy.h:92
Runtime serializer type-erased handle. Traversal logic is shared with compile-time serialization,...
Definition ser_rt.h:79