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 Component* comps, const ChunkDataOffsets& headerOffsets,
84 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 = comps[j];
108 dst[j].pData = &data(compOffs[j]);
109 dst[j].pItem = m_header.cc->find(comps[j].id());
110 }
111 }
112
113 m_records.pEntities = (Entity*)&data(headerOffsets.firstByte_EntityData);
114
115 // Now that records are set, we use the cached component descriptors to set ctor/dtor masks.
116 {
117 auto recs = comp_rec_view();
118 const auto recs_cnt = recs.size();
119 GAIA_FOR(recs_cnt) {
120 const auto& rec = recs[i];
121 if (!component_has_inline_data(rec.comp))
122 continue;
123
124 const auto e = m_records.pCompEntities[i];
125 if (e.kind() == EntityKind::EK_Gen) {
126 m_header.hasAnyCustomGenCtor |= (rec.pItem->func_ctor != nullptr);
127 m_header.hasAnyCustomGenDtor |= (rec.pItem->func_dtor != nullptr);
128 } else {
129 m_header.hasAnyCustomUniCtor |= (rec.pItem->func_ctor != nullptr);
130 m_header.hasAnyCustomUniDtor |= (rec.pItem->func_dtor != nullptr);
131
132 // We construct unique components right away if possible
133 call_ctor(0, *rec.pItem);
134 }
135 }
136 }
137
138 // Make sure world versions are set initially.
139 update_world_version_init();
140 }
141
142 GAIA_CLANG_WARNING_POP()
143
144
146 GAIA_NODISCARD std::span<const ComponentVersion> comp_version_view() const {
147 return {(const ComponentVersion*)m_records.pVersions, (size_t)m_header.cntEntities + 1};
148 }
149
152 GAIA_NODISCARD std::span<ComponentVersion> comp_version_view_mut() {
153 return {m_records.pVersions, (size_t)m_header.cntEntities + 1};
154 }
155
156 GAIA_NODISCARD std::span<Entity> entity_view_mut() {
157 return {m_records.pEntities, m_header.count};
158 }
159
167 template <typename T>
168 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) const //
169 -> decltype(std::span<const uint8_t>{}) {
170
171 if constexpr (std::is_same_v<core::raw_t<T>, Entity>) {
172 GAIA_ASSERT(to <= m_header.count);
173 return {(const uint8_t*)&m_records.pEntities[from], to - from};
174 } else if constexpr (is_pair<T>::value) {
175 using TT = typename T::type;
176 using U = typename component_type_t<TT>::Type;
177 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
178
179 constexpr auto kind = entity_kind_v<TT>;
180
181 if constexpr (mem::is_soa_layout_v<U>) {
182 GAIA_ASSERT(from == 0);
183 GAIA_ASSERT(to == capacity());
184 return {comp_ptr(compIdx), to};
185 } else if constexpr (kind == EntityKind::EK_Gen) {
186 GAIA_ASSERT(to <= m_header.count);
187 return {comp_ptr(compIdx, from), to - from};
188 } else {
189 GAIA_ASSERT(to <= m_header.count);
190 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
191 return {comp_ptr(compIdx), 1};
192 }
193 } else {
194 using U = typename component_type_t<T>::Type;
195 static_assert(!std::is_empty_v<U>, "Attempting to get value of an empty component");
196
197 constexpr auto kind = entity_kind_v<T>;
198
199 if constexpr (mem::is_soa_layout_v<U>) {
200 GAIA_ASSERT(from == 0);
201 GAIA_ASSERT(to == capacity());
202 return {comp_ptr(compIdx), to};
203 } else if constexpr (kind == EntityKind::EK_Gen) {
204 GAIA_ASSERT(to <= m_header.count);
205 return {comp_ptr(compIdx, from), to - from};
206 } else {
207 GAIA_ASSERT(to <= m_header.count);
208 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
209 return {comp_ptr(compIdx), 1};
210 }
211 }
212 }
213
220 template <typename T>
221 GAIA_NODISCARD GAIA_FORCEINLINE auto view_inter(uint32_t from, uint32_t to) const //
222 -> decltype(std::span<const uint8_t>{}) {
223 if constexpr (std::is_same_v<core::raw_t<T>, Entity>)
224 return view_inter_idx<T>(BadIndex, from, to);
225 else if constexpr (is_pair<T>::value) {
226 const auto rel = m_header.cc->get<typename T::rel>().entity;
227 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
228 return view_inter_idx<T>(comp_idx((Entity)Pair(rel, tgt)), from, to);
229 } else {
230 constexpr auto kind = entity_kind_v<T>;
231 const auto comp = m_header.cc->get<T>().entity;
232 GAIA_ASSERT(comp.kind() == kind);
233 return view_inter_idx<T>(comp_idx(comp), from, to);
234 }
235 }
236
245 template <typename T, bool WorldVersionUpdateWanted>
246 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter_idx(uint32_t compIdx, uint32_t from, uint32_t to) //
247 -> decltype(std::span<uint8_t>{}) {
248 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "view_mut can't be used to modify Entity");
249
250 if constexpr (is_pair<T>::value) {
251 using TT = typename T::type;
252 using U = typename component_type_t<TT>::Type;
253 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
254
255 constexpr auto kind = entity_kind_v<TT>;
256
257 // Update version number if necessary so we know RW access was used on the chunk
258 if constexpr (WorldVersionUpdateWanted) {
259 update_world_version(compIdx);
260
261#if GAIA_ENABLE_SET_HOOKS
262 const auto& rec = m_records.pRecords[compIdx];
263 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
264 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
265#endif
266 }
267
268 if constexpr (mem::is_soa_layout_v<U>) {
269 GAIA_ASSERT(from == 0);
270 GAIA_ASSERT(to == capacity());
271 return {comp_ptr_mut(compIdx), to};
272 } else if constexpr (kind == EntityKind::EK_Gen) {
273 GAIA_ASSERT(to <= m_header.count);
274 return {comp_ptr_mut(compIdx, from), to - from};
275 } else {
276 GAIA_ASSERT(to <= m_header.count);
277 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
278 return {comp_ptr_mut(compIdx), 1};
279 }
280 } else {
281 using U = typename component_type_t<T>::Type;
282 static_assert(!std::is_empty_v<U>, "view_mut can't be used to modify tag components");
283 constexpr auto kind = entity_kind_v<T>;
284
285 // Update version number if necessary so we know RW access was used on the chunk
286 if constexpr (WorldVersionUpdateWanted) {
287 update_world_version(compIdx);
288
289#if GAIA_ENABLE_SET_HOOKS
290 const auto& rec = m_records.pRecords[compIdx];
291 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
292 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
293#endif
294 }
295
296 if constexpr (mem::is_soa_layout_v<U>) {
297 GAIA_ASSERT(from == 0);
298 GAIA_ASSERT(to == capacity());
299 return {comp_ptr_mut(compIdx), to};
300 } else if constexpr (kind == EntityKind::EK_Gen) {
301 GAIA_ASSERT(to <= m_header.count);
302 return {comp_ptr_mut(compIdx, from), to - from};
303 } else {
304 GAIA_ASSERT(to <= m_header.count);
305 // GAIA_ASSERT(count == 1); we don't really care and always consider 1 for unique components
306 return {comp_ptr_mut(compIdx), 1};
307 }
308 }
309 }
310
311 template <typename T, bool WorldVersionUpdateWanted>
312 GAIA_NODISCARD GAIA_FORCEINLINE auto view_mut_inter(uint32_t from, uint32_t to) //
313 -> decltype(std::span<uint8_t>{}) {
314 if constexpr (is_pair<T>::value) {
315 const auto rel = m_header.cc->get<typename T::rel>().entity;
316 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
317 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx((Entity)Pair(rel, tgt)), from, to);
318 } else {
319 constexpr auto kind = entity_kind_v<T>;
320 const auto comp = m_header.cc->get<T>().entity;
321 GAIA_ASSERT(comp.kind() == kind);
322 return view_mut_inter_idx<T, WorldVersionUpdateWanted>(comp_idx(comp), from, to);
323 }
324 }
325
326 public:
333 template <bool WorldVersionUpdateWanted>
334 GAIA_NODISCARD GAIA_FORCEINLINE auto comp_ptr_mut_gen(uint32_t compIdx, uint32_t row) {
335 // Update version number if necessary so we know RW access was used on the chunk
336 if constexpr (WorldVersionUpdateWanted) {
337 update_world_version(compIdx);
338
339#if GAIA_ENABLE_SET_HOOKS
340 const auto& rec = m_records.pRecords[compIdx];
341 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
342 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
343#endif
344 }
345
346 return comp_ptr_mut(compIdx, row);
347 }
348
351 void finish_write(uint32_t compIdx, uint16_t from, uint16_t to) {
352 GAIA_ASSERT(compIdx < m_header.cntEntities);
353 if (from >= to)
354 return;
355
356 update_world_version(compIdx);
357
358#if GAIA_ENABLE_SET_HOOKS
359 const auto& rec = m_records.pRecords[compIdx];
360 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
361 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
362#endif
363
364 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, from, to);
365 }
366
367 private:
374 template <typename T>
375 GAIA_NODISCARD decltype(auto) comp_inter(uint16_t row) const {
376 using U = typename actual_type_t<T>::Type;
377 using RetValueType = decltype(view<T>()[0]);
378
379 GAIA_ASSERT(row < m_header.count);
380 if constexpr (mem::is_soa_layout_v<U>)
381 return view<T>(0, capacity())[row];
382 else if constexpr (sizeof(RetValueType) <= 8)
383 return view<T>()[row];
384 else
385 return (const U&)view<T>()[row];
386 }
387
388 template <typename T>
389 GAIA_NODISCARD decltype(auto) comp_inter_idx(uint16_t row, uint32_t compIdx) const {
390 using U = typename actual_type_t<T>::Type;
391 using RetValueType = decltype(view_raw<T>((const void*)nullptr, 1)[0]);
392
393 GAIA_ASSERT(row < m_header.count);
394 if constexpr (mem::is_soa_layout_v<U>)
395 return view_raw<T>(comp_ptr(compIdx), capacity())[row];
396 else if constexpr (entity_kind_v<T> == EntityKind::EK_Gen) {
397 if constexpr (sizeof(RetValueType) <= 8)
398 return view_raw<T>(comp_ptr(compIdx, row), 1)[0];
399 else
400 return (const U&)view_raw<T>(comp_ptr(compIdx, row), 1)[0];
401 } else {
402 if constexpr (sizeof(RetValueType) <= 8)
403 return view_raw<T>(comp_ptr(compIdx), 1)[0];
404 else
405 return (const U&)view_raw<T>(comp_ptr(compIdx), 1)[0];
406 }
407 }
408
409 template <typename T, bool WorldVersionUpdateWanted>
410 GAIA_NODISCARD decltype(auto) comp_mut_idx(uint16_t row, uint32_t compIdx) {
411 using U = typename actual_type_t<T>::Type;
412
413 GAIA_ASSERT(row < m_header.capacity);
414 if constexpr (mem::is_soa_layout_v<U>)
415 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), capacity())[row];
416 else if constexpr (entity_kind_v<T> == EntityKind::EK_Gen)
417 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, row), 1)[0];
418 else
419 return view_mut_raw<T>(comp_ptr_mut_gen<WorldVersionUpdateWanted>(compIdx, 0), 1)[0];
420 }
421
422 public:
423 Chunk(const Chunk& chunk) = delete;
424 Chunk(Chunk&& chunk) = delete;
425 Chunk& operator=(const Chunk& chunk) = delete;
426 Chunk& operator=(Chunk&& chunk) = delete;
427 ~Chunk() = default;
428
429 static constexpr uint16_t chunk_header_size() {
430 const auto dataAreaOffset =
431 // ChunkAllocator reserves the first few bytes for internal purposes
432 MemoryBlockUsableOffset +
433 // Chunk "header" area (before actual entity/component data starts)
434 sizeof(ChunkHeader) + sizeof(ChunkRecords);
435 static_assert(dataAreaOffset < UINT16_MAX);
436 return dataAreaOffset;
437 }
438
439 static constexpr uint16_t chunk_total_bytes(uint16_t dataSize) {
440 return chunk_header_size() + dataSize;
441 }
442
443 static constexpr uint16_t chunk_data_bytes(uint16_t totalSize) {
444 return totalSize - chunk_header_size();
445 }
446
448 static uintptr_t chunk_data_area_offset() {
449 // Note, offsetof is implementation-defined and conditionally-supported since C++17.
450 // Therefore, we instantiate the chunk and calculate the relative address ourselves.
451 Chunk chunk;
452 const auto chunk_offset = (uintptr_t)&chunk;
453 const auto data_offset = (uintptr_t)&chunk.m_data[0];
454 return data_offset - chunk_offset;
455 }
456
459 static Chunk* create(
460 const World& wld, const ComponentCache& cc, //
461 uint32_t chunkIndex, uint16_t capacity, uint8_t cntEntities, uint8_t genEntities, //
462 uint16_t dataBytes, uint32_t& worldVersion,
463 // data offsets
464 const ChunkDataOffsets& offsets,
465 // component entities
466 const Entity* ids,
467 // component
468 const Component* comps,
469 // component offsets
470 const ChunkDataOffset* compOffs) {
471 const auto totalBytes = chunk_total_bytes(dataBytes);
472#if GAIA_ECS_CHUNK_ALLOCATOR
473 auto* pChunk = (Chunk*)ChunkAllocator::get().alloc(totalBytes);
474 (void)new (pChunk) Chunk(wld, cc, chunkIndex, capacity, genEntities, worldVersion);
475#else
476 GAIA_ASSERT(totalBytes <= MaxMemoryBlockSize);
477 const auto sizeType = mem_block_size_type(totalBytes);
478 const auto allocSize = mem_block_size(sizeType);
479 auto* pChunkMem = mem::AllocHelper::alloc<uint8_t>(allocSize);
480 std::memset(pChunkMem, 0, allocSize);
481 auto* pChunk = new (pChunkMem) Chunk(wld, cc, chunkIndex, capacity, genEntities, worldVersion);
482#endif
483
484 pChunk->init((uint32_t)cntEntities, ids, comps, offsets, compOffs);
485 return pChunk;
486 }
487
490 static void free(Chunk* pChunk) {
491 GAIA_ASSERT(pChunk != nullptr);
492 GAIA_ASSERT(!pChunk->dead());
493
494 // Mark as dead
495 pChunk->die();
496
497 // Call destructors for components that need it
498 pChunk->call_all_dtors();
499
500 pChunk->~Chunk();
501#if GAIA_ECS_CHUNK_ALLOCATOR
502 ChunkAllocator::get().free(pChunk);
503#else
504 mem::AllocHelper::free((uint8_t*)pChunk);
505#endif
506 }
507
508 void save(ser::serializer& s) {
509 s.save(m_header.count);
510 if (m_header.count == 0)
511 return;
512
513 s.save(m_header.countEnabled);
514
515 const uint16_t dead = m_header.dead;
516 const uint16_t lifespanCountdown = m_header.lifespanCountdown;
517 s.save(dead);
518 s.save(lifespanCountdown);
519
520 const auto cnt = (uint32_t)m_header.count;
521 const auto cap = (uint32_t)m_header.capacity;
522
523 // Store entity data
524 {
525 const auto* pData = m_records.pEntities;
526 GAIA_FOR(cnt) s.save(pData[i]);
527 }
528
529 // Store component data
530 {
531 for (const auto& rec: comp_rec_view()) {
532 // Skip the component if there's no size associated with it
533 if (!component_has_inline_data(rec.comp))
534 continue;
535
536 rec.pItem->save(s, rec.pData, 0, cnt, cap);
537 }
538 }
539 }
540
541 void load(ser::serializer& s) {
542 uint16_t prevCount = m_header.count;
543 s.load(m_header.count);
544 if (m_header.count == 0)
545 return;
546
547 s.load(m_header.countEnabled);
548
549 uint16_t dead = 0;
550 uint16_t lifespanCountdown = 0;
551 s.load(dead);
552 s.load(lifespanCountdown);
553 m_header.dead = dead != 0;
554 m_header.lifespanCountdown = lifespanCountdown;
555
556 const auto cnt = (uint32_t)m_header.count;
557 const auto cap = (uint32_t)m_header.capacity;
558
559 // Load entity data
560 {
561 GAIA_FOR(cnt) {
562 Entity e;
563 s.load(e);
564 entity_view_mut()[i] = e;
565 }
566 }
567
568 // Load component data. Call constructors first as necessary.
569 call_gen_ctors(prevCount, cnt);
570 {
571 for (const auto& rec: comp_rec_view()) {
572 // Skip the component if there's no size associated with it
573 if (!component_has_inline_data(rec.comp))
574 continue;
575
576 rec.pItem->load(s, rec.pData, 0, cnt, cap);
577 }
578 }
579 }
580
584 // Should never be called over an empty chunk
585 GAIA_ASSERT(!empty());
586
587#if GAIA_ASSERT_ENABLED
588 // Invalidate the entity in chunk data
589 entity_view_mut()[m_header.count - 1] = EntityBad;
590#endif
591
592 --m_header.count;
593 }
594
597 ::gaia::ecs::update_version(m_header.worldVersion);
598 update_world_version();
599 update_entity_order_version();
600 }
601
608 template <typename T>
609 GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const {
610 using U = typename actual_type_t<T>::Type;
611
612 // Always consider full range for SoA
613 if constexpr (mem::is_soa_layout_v<U>)
614 return mem::auto_view_policy_get<U>{view_inter<T>(0, capacity())};
615 else
616 return mem::auto_view_policy_get<U>{view_inter<T>(from, to)};
617 }
618
619 template <typename T>
620 GAIA_NODISCARD decltype(auto) view() const {
621 return view<T>(0, m_header.count);
622 }
623
624 template <typename T>
625 GAIA_NODISCARD decltype(auto) view_raw(const void* ptr, uint32_t size) const {
626 using U = typename actual_type_t<T>::Type;
627 return mem::auto_view_policy_get<U>{std::span{(const uint8_t*)ptr, size}};
628 }
629
636 template <typename T>
637 GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to) {
638 using U = typename actual_type_t<T>::Type;
639 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
640
641 // Always consider full range for SoA
642 if constexpr (mem::is_soa_layout_v<U>)
643 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(0, capacity())};
644 else
645 return mem::auto_view_policy_set<U>{view_mut_inter<T, true>(from, to)};
646 }
647
648 template <typename T>
649 GAIA_NODISCARD decltype(auto) view_mut() {
650 return view_mut<T>(0, m_header.count);
651 }
652
653 template <typename T>
654 GAIA_NODISCARD decltype(auto) view_mut_raw(void* ptr, uint32_t size) const {
655 using U = typename actual_type_t<T>::Type;
656 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via view_mut is forbidden");
657
658 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
659 }
660
668 template <typename T>
669 GAIA_NODISCARD decltype(auto) sview_mut(uint16_t from, uint16_t to) {
670 using U = typename actual_type_t<T>::Type;
671 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
672
673 // Always consider full range for SoA
674 if constexpr (mem::is_soa_layout_v<U>)
675 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(0, capacity())};
676 else
677 return mem::auto_view_policy_set<U>{view_mut_inter<T, false>(from, to)};
678 }
679
680 template <typename T>
681 GAIA_NODISCARD decltype(auto) sview_mut_raw(void* ptr, uint32_t size) const {
682 using U = typename actual_type_t<T>::Type;
683 static_assert(!std::is_same_v<U, Entity>, "Modifying chunk entities via sview_mut is forbidden");
684
685 return mem::auto_view_policy_set<U>{std::span{(uint8_t*)ptr, size}};
686 }
687
688 template <typename T>
689 GAIA_NODISCARD decltype(auto) sview_mut() {
690 return sview_mut<T>(0, m_header.count);
691 }
692
696 template <
697 typename T
698#if GAIA_ENABLE_HOOKS
699 ,
700 bool TriggerSetHooks
701#endif
702 >
703 GAIA_FORCEINLINE void modify() {
704 static_assert(!std::is_same_v<core::raw_t<T>, Entity>, "mod can't be used to modify Entity");
705
706 if constexpr (is_pair<T>::value) {
707 using TT = typename T::type;
708 using U = typename component_type_t<TT>::Type;
709 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
710
711#if GAIA_ASSERT_ENABLED
712 // constexpr auto kind = entity_kind_v<TT>;
713#endif
714 const auto rel = m_header.cc->get<typename T::rel>().entity;
715 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
716 const auto compIdx = comp_idx((Entity)Pair(rel, tgt));
717
718 // Update version number if necessary so we know RW access was used on the chunk
719 update_world_version(compIdx);
720
721#if GAIA_ENABLE_SET_HOOKS
722 if constexpr (TriggerSetHooks) {
723 const auto& rec = m_records.pRecords[compIdx];
724 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
725 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
726 }
727#endif
728 } else {
729 using U = typename component_type_t<T>::Type;
730 static_assert(!std::is_empty_v<U>, "mut can't be used to modify tag components");
731
732#if GAIA_ASSERT_ENABLED
733 constexpr auto kind = entity_kind_v<T>;
734#endif
735 const auto comp = m_header.cc->get<T>().entity;
736 GAIA_ASSERT(comp.kind() == kind);
737 const auto compIdx = comp_idx(comp);
738
739 // Update version number if necessary so we know RW access was used on the chunk
740 update_world_version(compIdx);
741
742#if GAIA_ENABLE_SET_HOOKS
743 if constexpr (TriggerSetHooks) {
744 const auto& rec = m_records.pRecords[compIdx];
745 if GAIA_UNLIKELY (rec.pItem->comp_hooks.func_set != nullptr)
746 rec.pItem->comp_hooks.func_set(*m_header.world, rec, *this);
747 }
748#endif
749 }
750 }
751
759 template <typename T>
760 GAIA_NODISCARD decltype(auto) view_auto(uint16_t from, uint16_t to) {
761 using UOriginal = typename actual_type_t<T>::TypeOriginal;
762 if constexpr (core::is_mut_v<UOriginal>)
763 return view_mut<T>(from, to);
764 else
765 return view<T>(from, to);
766 }
767
768 template <typename T>
769 GAIA_NODISCARD decltype(auto) view_auto() {
770 return view_auto<T>(0, m_header.count);
771 }
772
781 template <typename T>
782 GAIA_NODISCARD decltype(auto) sview_auto(uint16_t from, uint16_t to) {
783 using UOriginal = typename actual_type_t<T>::TypeOriginal;
784 if constexpr (core::is_mut_v<UOriginal>)
785 return sview_mut<T>(from, to);
786 else
787 return view<T>(from, to);
788 }
789
790 template <typename T>
791 GAIA_NODISCARD decltype(auto) sview_auto() {
792 return sview_auto<T>(0, m_header.count);
793 }
794
795 GAIA_NODISCARD EntitySpan entity_view() const {
796 return {(const Entity*)m_records.pEntities, m_header.count};
797 }
798
799 GAIA_NODISCARD World& world() {
800 return *const_cast<World*>(m_header.world);
801 }
802
803 GAIA_NODISCARD const World& world() const {
804 return *m_header.world;
805 }
806
807 GAIA_NODISCARD EntitySpan ids_view() const {
808 return {(const Entity*)m_records.pCompEntities, m_header.cntEntities};
809 }
810
811 GAIA_NODISCARD std::span<const ComponentRecord> comp_rec_view() const {
812 return {m_records.pRecords, m_header.cntEntities};
813 }
814
815 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx) {
816 const auto& rec = m_records.pRecords[compIdx];
817 return rec.pData;
818 }
819
820 GAIA_NODISCARD uint8_t* comp_ptr_mut(uint32_t compIdx, uint32_t offset) {
821 const auto& rec = m_records.pRecords[compIdx];
822 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
823 }
824
825 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx) const {
826 const auto& rec = m_records.pRecords[compIdx];
827 return rec.pData;
828 }
829
830 GAIA_NODISCARD const uint8_t* comp_ptr(uint32_t compIdx, uint32_t offset) const {
831 const auto& rec = m_records.pRecords[compIdx];
832 return rec.pData + ((uintptr_t)rec.comp.size() * offset);
833 }
834
837 GAIA_NODISCARD uint16_t add_entity(Entity entity) {
838 const auto row = m_header.count++;
839
840 // Zero after increase of value means an overflow!
841 GAIA_ASSERT(m_header.count != 0);
842
843 ++m_header.countEnabled;
844 entity_view_mut()[row] = entity;
845
846 return row;
847 }
848
853 static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers& recs) {
854 GAIA_PROF_SCOPE(Chunk::copy_entity_data);
855
856 auto& srcEntityContainer = recs[srcEntity];
857 auto* pSrcChunk = srcEntityContainer.pChunk;
858
859 auto& dstEntityContainer = recs[dstEntity];
860 auto* pDstChunk = dstEntityContainer.pChunk;
861
862 GAIA_ASSERT(srcEntityContainer.pArchetype == dstEntityContainer.pArchetype);
863
864 auto srcRecs = pSrcChunk->comp_rec_view();
865
866 // Copy generic component data from reference entity to our new entity.
867 // Unique components do not change place in the chunk so there is no need to move them.
868 GAIA_FOR(pSrcChunk->m_header.genEntities) {
869 const auto& rec = srcRecs[i];
870 if (!component_has_inline_data(rec.comp))
871 continue;
872
873 const auto* pSrc = (const void*)pSrcChunk->comp_ptr_mut(i);
874 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
875 rec.pItem->copy(
876 pDst, pSrc, dstEntityContainer.row, srcEntityContainer.row, pDstChunk->capacity(), pSrcChunk->capacity());
877 }
878 }
879
887 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
888 GAIA_PROF_SCOPE(Chunk::copy_entity_data_n_same_chunk);
889
890 GAIA_ASSERT(pSrcChunk != nullptr);
891 GAIA_ASSERT(pDstChunk != nullptr);
892 GAIA_ASSERT(srcRow < pSrcChunk->size());
893 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
894 GAIA_ASSERT(pSrcChunk->ids_view().size() == pDstChunk->ids_view().size());
895
896 auto srcRecs = pSrcChunk->comp_rec_view();
897
898 // Copy generic component data from the reference entity to all newly allocated rows.
899 // Unique components do not change place in the chunk so there is no need to move them.
900 GAIA_FOR(pSrcChunk->m_header.genEntities) {
901 const auto& rec = srcRecs[i];
902 if (!component_has_inline_data(rec.comp))
903 continue;
904
905 const auto* pSrc = (const void*)pSrcChunk->comp_ptr(i);
906 GAIA_FOR_(dstCount, rowOffset) {
907 auto* pDst = (void*)pDstChunk->comp_ptr_mut(i);
908 rec.pItem->copy(pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
909 }
910 }
911 }
912
920 Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow, uint32_t dstCount) {
921 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data_n);
922
923 GAIA_ASSERT(pSrcChunk != nullptr);
924 GAIA_ASSERT(pDstChunk != nullptr);
925 GAIA_ASSERT(srcRow < pSrcChunk->size());
926 GAIA_ASSERT(dstRow + dstCount <= pDstChunk->size());
927
928 auto srcIds = pSrcChunk->ids_view();
929 auto dstIds = pDstChunk->ids_view();
930 auto dstRecs = pDstChunk->comp_rec_view();
931
932 uint32_t i = 0;
933 uint32_t j = 0;
934 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
935 const auto oldId = srcIds[i];
936 const auto newId = dstIds[j];
937
938 if (oldId == newId) {
939 const auto& rec = dstRecs[j];
940 if (component_has_inline_data(rec.comp)) {
941 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
942 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
943 GAIA_FOR_(dstCount, rowOffset) {
944 rec.pItem->ctor_copy(
945 pDst, pSrc, dstRow + rowOffset, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
946 }
947 }
948
949 ++i;
950 ++j;
951 } else if (SortComponentCond{}.operator()(oldId, newId)) {
952 ++i;
953 } else {
954 const auto& rec = dstRecs[j];
955 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
956 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
957 rec.pItem->func_ctor(pDst, dstCount);
958 }
959
960 ++j;
961 }
962 }
963
964 for (; j < pDstChunk->m_header.genEntities; ++j) {
965 const auto& rec = dstRecs[j];
966 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
967 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
968 rec.pItem->func_ctor(pDst, dstCount);
969 }
970 }
971 }
972
977 void move_entity_data(Entity entity, uint16_t row, EntityContainers& recs) {
978 GAIA_PROF_SCOPE(Chunk::move_entity_data);
979
980 auto& ec = recs[entity];
981 auto* pSrcChunk = ec.pChunk;
982 auto srcRecs = pSrcChunk->comp_rec_view();
983
984 // Copy generic component data from reference entity to our new entity.
985 // Unique components do not change place in the chunk so there is no need to move them.
986 GAIA_FOR(pSrcChunk->m_header.genEntities) {
987 const auto& rec = srcRecs[i];
988 if (!component_has_inline_data(rec.comp))
989 continue;
990
991 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
992 auto* pDst = (void*)comp_ptr_mut(i);
993 rec.pItem->ctor_move(pDst, pSrc, row, ec.row, capacity(), pSrcChunk->capacity());
994 }
995 }
996
1002 static void copy_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1003 GAIA_PROF_SCOPE(Chunk::copy_foreign_entity_data);
1004
1005 GAIA_ASSERT(pSrcChunk != nullptr);
1006 GAIA_ASSERT(pDstChunk != nullptr);
1007 GAIA_ASSERT(srcRow < pSrcChunk->size());
1008 GAIA_ASSERT(dstRow < pDstChunk->size());
1009
1010 auto srcIds = pSrcChunk->ids_view();
1011 auto dstIds = pDstChunk->ids_view();
1012 auto dstRecs = pDstChunk->comp_rec_view();
1013
1014 // Find intersection of the two component lists.
1015 // Arrays are sorted so we can do linear intersection lookup.
1016 // Call constructor on each match.
1017 // Unique components do not change place in the chunk so there is no need to move them.
1018 {
1019 uint32_t i = 0;
1020 uint32_t j = 0;
1021 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1022 const auto oldId = srcIds[i];
1023 const auto newId = dstIds[j];
1024
1025 if (oldId == newId) {
1026 const auto& rec = dstRecs[j];
1027 if (component_has_inline_data(rec.comp)) {
1028 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1029 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1030 rec.pItem->ctor_copy(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1031 }
1032
1033 ++i;
1034 ++j;
1035 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1036 ++i;
1037 } else {
1038 // No match with the old chunk. Construct the component
1039 const auto& rec = dstRecs[j];
1040 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1041 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1042 rec.pItem->func_ctor(pDst, 1);
1043 }
1044
1045 ++j;
1046 }
1047 }
1048
1049 // Initialize the rest of the components if they are generic.
1050 for (; j < pDstChunk->m_header.genEntities; ++j) {
1051 const auto& rec = dstRecs[j];
1052 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1053 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1054 rec.pItem->func_ctor(pDst, 1);
1055 }
1056 }
1057 }
1058 }
1059
1065 static void move_foreign_entity_data(Chunk* pSrcChunk, uint32_t srcRow, Chunk* pDstChunk, uint32_t dstRow) {
1066 GAIA_PROF_SCOPE(Chunk::move_foreign_entity_data);
1067
1068 GAIA_ASSERT(pSrcChunk != nullptr);
1069 GAIA_ASSERT(pDstChunk != nullptr);
1070 GAIA_ASSERT(srcRow < pSrcChunk->size());
1071 GAIA_ASSERT(dstRow < pDstChunk->size());
1072
1073 auto srcIds = pSrcChunk->ids_view();
1074 auto dstIds = pDstChunk->ids_view();
1075 auto dstRecs = pDstChunk->comp_rec_view();
1076
1077 // Find intersection of the two component lists.
1078 // Arrays are sorted so we can do linear intersection lookup.
1079 // Call constructor on each match.
1080 // Unique components do not change place in the chunk so there is no need to move them.
1081 {
1082 uint32_t i = 0;
1083 uint32_t j = 0;
1084 while (i < pSrcChunk->m_header.genEntities && j < pDstChunk->m_header.genEntities) {
1085 const auto oldId = srcIds[i];
1086 const auto newId = dstIds[j];
1087
1088 if (oldId == newId) {
1089 const auto& rec = dstRecs[j];
1090 if (component_has_inline_data(rec.comp)) {
1091 auto* pSrc = (void*)pSrcChunk->comp_ptr_mut(i);
1092 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j);
1093 rec.pItem->ctor_move(pDst, pSrc, dstRow, srcRow, pDstChunk->capacity(), pSrcChunk->capacity());
1094 }
1095
1096 ++i;
1097 ++j;
1098 } else if (SortComponentCond{}.operator()(oldId, newId)) {
1099 ++i;
1100 } else {
1101 // No match with the old chunk. Construct the component
1102 const auto& rec = dstRecs[j];
1103 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1104 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1105 rec.pItem->func_ctor(pDst, 1);
1106 }
1107
1108 ++j;
1109 }
1110 }
1111
1112 // Initialize the rest of the components if they are generic.
1113 for (; j < pDstChunk->m_header.genEntities; ++j) {
1114 const auto& rec = dstRecs[j];
1115 if (rec.pItem != nullptr && rec.pItem->func_ctor != nullptr) {
1116 auto* pDst = (void*)pDstChunk->comp_ptr_mut(j, dstRow);
1117 rec.pItem->func_ctor(pDst, 1);
1118 }
1119 }
1120 }
1121 }
1122
1129 void remove_entity_inter(uint16_t row, EntityContainers& recs) {
1130 GAIA_PROF_SCOPE(Chunk::remove_entity_inter);
1131
1132 const uint16_t rowA = row;
1133 const uint16_t rowB = m_header.count - 1;
1134 // The "rowA" entity is the one we are going to destroy so it needs to precede the "rowB"
1135 GAIA_ASSERT(rowA <= rowB);
1136
1137 // To move anything, we need at least 2 entities
1138 if GAIA_LIKELY (rowA < rowB) {
1139 GAIA_ASSERT(m_header.count > 1);
1140
1141 auto ev = entity_view_mut();
1142
1143 // Update entity data
1144 const auto entityB = ev[rowB];
1145 auto& ecB = recs[entityB];
1146#if GAIA_ASSERT_ENABLED
1147 const auto entityA = ev[rowA];
1148 auto& ecA = recs[entityA];
1149
1150 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1151 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1152#endif
1153
1154 ev[rowA] = entityB;
1155
1156 // Move component data from entityB to entityA
1157 auto recView = comp_rec_view();
1158 GAIA_FOR(m_header.genEntities) {
1159 const auto& rec = recView[i];
1160 if (!component_has_inline_data(rec.comp))
1161 continue;
1162
1163 auto* pSrc = (void*)comp_ptr_mut(i);
1164 rec.pItem->move(pSrc, pSrc, rowA, rowB, capacity(), capacity());
1165
1166 pSrc = (void*)comp_ptr_mut(i, rowB);
1167 rec.pItem->dtor(pSrc);
1168 }
1169
1170 // Entity has been replaced with the last one in our chunk. Update its container record.
1171 ecB.row = rowA;
1172 ecB.pEntity = &ev[rowA];
1173 } else if (m_header.hasAnyCustomGenDtor) {
1174 // This is the last entity in the chunk so simply destroy its data
1175 auto recView = comp_rec_view();
1176 GAIA_FOR(m_header.genEntities) {
1177 const auto& rec = recView[i];
1178 if (!component_has_inline_data(rec.comp))
1179 continue;
1180
1181 auto* pSrc = (void*)comp_ptr_mut(i, rowA);
1182 rec.pItem->dtor(pSrc);
1183 }
1184 }
1185 }
1186
1193 void remove_entity(uint16_t row, EntityContainers& recs) {
1194 if GAIA_UNLIKELY (m_header.count == 0)
1195 return;
1196
1197 GAIA_PROF_SCOPE(Chunk::remove_entity);
1198
1199 if (enabled(row)) {
1200 // Entity was previously enabled. Swap with the last entity
1201 remove_entity_inter(row, recs);
1202 // If this was the first enabled entity make sure to update the row
1203 if (m_header.rowFirstEnabledEntity > 0 && row == m_header.rowFirstEnabledEntity)
1204 --m_header.rowFirstEnabledEntity;
1205 // At this point the last entity is no longer valid so remove it
1206 remove_last_entity();
1207 --m_header.countEnabled;
1208 } else {
1209 // Entity was previously disabled. Swap with the last disabled entity
1210 const uint16_t pivot = size_disabled() - 1;
1211 swap_chunk_entities(row, pivot, recs);
1212 // Once swapped, try to swap with the last (enabled) entity in the chunk.
1213 remove_entity_inter(pivot, recs);
1214 --m_header.rowFirstEnabledEntity;
1215 // At this point the last entity is no longer valid so remove it
1216 remove_last_entity();
1217 }
1218 }
1219
1227 void swap_chunk_entities(uint16_t rowA, uint16_t rowB, EntityContainers& recs) {
1228 // If there are at least two different entities inside to swap
1229 if GAIA_UNLIKELY (m_header.count <= 1 || rowA == rowB)
1230 return;
1231
1232 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1233
1234 // Update entity data
1235 auto ev = entity_view_mut();
1236 const auto entityA = ev[rowA];
1237 const auto entityB = ev[rowB];
1238
1239 auto& ecA = recs[entityA];
1240 auto& ecB = recs[entityB];
1241 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1242 GAIA_ASSERT(ecA.pChunk == ecB.pChunk);
1243
1244 ev[rowA] = entityB;
1245 ev[rowB] = entityA;
1246
1247 // Swap component data
1248 auto recView = comp_rec_view();
1249 GAIA_FOR(m_header.genEntities) {
1250 const auto& rec = recView[i];
1251 if (!component_has_inline_data(rec.comp))
1252 continue;
1253
1254 GAIA_ASSERT(rec.pData == comp_ptr_mut(i));
1255 rec.pItem->swap(rec.pData, rec.pData, rowA, rowB, capacity(), capacity());
1256 }
1257
1258 // Update indices in entity container.
1259 ecA.row = rowB;
1260 ecB.row = rowA;
1261 ecA.pEntity = &ev[rowB];
1262 ecB.pEntity = &ev[rowA];
1263 }
1264
1271 static void swap_chunk_entities(World& world, Entity entityA, Entity entityB) {
1272 // Don't swap if the two entities are the same
1273 if GAIA_UNLIKELY (entityA == entityB)
1274 return;
1275
1276 GAIA_PROF_SCOPE(Chunk::swap_chunk_entities);
1277
1278 auto& ecA = fetch_mut(world, entityA);
1279 auto& ecB = fetch_mut(world, entityB);
1280
1281 // Make sure the two entities are in the same archetype
1282 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1283 GAIA_ASSERT(ecA.pArchetype == ecB.pArchetype);
1284
1285 auto* pChunkA = ecA.pChunk;
1286 auto* pChunkB = ecB.pChunk;
1287
1288 // Swap entities in the entity data part
1289 pChunkA->entity_view_mut()[ecA.row] = entityB;
1290 pChunkB->entity_view_mut()[ecB.row] = entityA;
1291
1292 // Swap component data
1293 auto recViewA = pChunkA->comp_rec_view();
1294 GAIA_FOR(pChunkA->m_header.genEntities) {
1295 const auto& recA = recViewA[i];
1296 if (!component_has_inline_data(recA.comp))
1297 continue;
1298
1299 auto* pDataA = pChunkA->comp_rec_view()[i].pData;
1300 auto* pDataB = pChunkB->comp_rec_view()[i].pData;
1301 recA.pItem->swap(
1302 // Data pointers
1303 pDataA, pDataB,
1304 // Rows
1305 ecA.row, ecB.row,
1306 // Chunk capacities
1307 pChunkA->capacity(), pChunkA->capacity() //
1308 );
1309 }
1310
1311 // Update indices and chunks in entity container.
1312 core::swap(ecA.row, ecB.row);
1313 core::swap(ecA.pChunk, ecB.pChunk);
1314 }
1315
1320 void enable_entity(uint16_t row, bool enableEntity, EntityContainers& recs) {
1321 GAIA_ASSERT(row < m_header.count && "Entity chunk row out of bounds!");
1322
1323 if (enableEntity) {
1324 // Nothing to enable if there are no disabled entities
1325 if (!m_header.has_disabled_entities())
1326 return;
1327 // Trying to enable an already enabled entity
1328 if (enabled(row))
1329 return;
1330 // Try swapping our entity with the last disabled one
1331 const auto entity = entity_view()[row];
1332 swap_chunk_entities(--m_header.rowFirstEnabledEntity, row, recs);
1333 recs[entity].data.dis = 0;
1334 ++m_header.countEnabled;
1335 } else {
1336 // Nothing to disable if there are no enabled entities
1337 if (!m_header.has_enabled_entities())
1338 return;
1339 // Trying to disable an already disabled entity
1340 if (!enabled(row))
1341 return;
1342 // Try swapping our entity with the last one in our chunk
1343 const auto entity = entity_view()[row];
1344 swap_chunk_entities(m_header.rowFirstEnabledEntity++, row, recs);
1345 recs[entity].data.dis = 1;
1346 --m_header.countEnabled;
1347 }
1348 }
1349
1353 bool enabled(uint16_t row) const {
1354 GAIA_ASSERT(m_header.count > 0);
1355
1356 return row >= (uint16_t)m_header.rowFirstEnabledEntity;
1357 }
1358
1362 uint8_t& data(uint32_t offset) {
1363 return m_data[offset];
1364 }
1365
1369 const uint8_t& data(uint32_t offset) const {
1370 return m_data[offset];
1371 }
1372
1373 //----------------------------------------------------------------------
1374 // Component handling
1375 //----------------------------------------------------------------------
1376
1377 void call_ctor(uint32_t entIdx, const ComponentCacheItem& item) {
1378 if (item.func_ctor == nullptr || !component_has_inline_data(item.comp))
1379 return;
1380
1381 GAIA_PROF_SCOPE(Chunk::call_ctor);
1382
1383 const auto compIdx = comp_idx(item.entity);
1384 auto* pSrc = (void*)comp_ptr_mut(compIdx, entIdx);
1385 item.func_ctor(pSrc, 1);
1386 }
1387
1388 void call_gen_ctors(uint32_t entIdx, uint32_t entCnt) {
1389 if (!m_header.hasAnyCustomGenCtor)
1390 return;
1391
1392 GAIA_PROF_SCOPE(Chunk::call_gen_ctors);
1393
1394 auto recs = comp_rec_view();
1395 GAIA_FOR(m_header.genEntities) {
1396 const auto& rec = recs[i];
1397 if (!component_has_inline_data(rec.comp))
1398 continue;
1399
1400 const auto* pItem = rec.pItem;
1401 if (pItem == nullptr || pItem->func_ctor == nullptr)
1402 continue;
1403
1404 auto* pSrc = (void*)comp_ptr_mut(i, entIdx);
1405 pItem->func_ctor(pSrc, entCnt);
1406 }
1407 }
1408
1409 void call_all_dtors() {
1410 if (!m_header.hasAnyCustomGenDtor && !m_header.hasAnyCustomUniCtor)
1411 return;
1412
1413 GAIA_PROF_SCOPE(Chunk::call_all_dtors);
1414
1415 auto ids = ids_view();
1416 auto recs = comp_rec_view();
1417 const auto recs_cnt = recs.size();
1418 GAIA_FOR(recs_cnt) {
1419 const auto& rec = recs[i];
1420 if (!component_has_inline_data(rec.comp))
1421 continue;
1422
1423 const auto* pItem = rec.pItem;
1424 if (pItem == nullptr || pItem->func_dtor == nullptr)
1425 continue;
1426
1427 auto* pSrc = (void*)comp_ptr_mut(i, 0);
1428 const auto e = ids[i];
1429 const auto cnt = (e.kind() == EntityKind::EK_Gen) ? m_header.count : (uint16_t)1;
1430 pItem->func_dtor(pSrc, cnt);
1431 }
1432 };
1433
1434 //----------------------------------------------------------------------
1435 // Check component presence
1436 //----------------------------------------------------------------------
1437
1441 GAIA_NODISCARD bool has(Entity entity) const {
1442 auto ids = ids_view();
1443 return core::has(ids, entity);
1444 }
1445
1449 template <typename T>
1450 GAIA_NODISCARD bool has() const {
1451 if constexpr (is_pair<T>::value) {
1452 const auto rel = m_header.cc->get<typename T::rel>().entity;
1453 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1454 return has((Entity)Pair(rel, tgt));
1455 } else {
1456 const auto* pComp = m_header.cc->find<T>();
1457 return pComp != nullptr && has(pComp->entity);
1458 }
1459 }
1460
1461 //----------------------------------------------------------------------
1462 // Set component data
1463 //----------------------------------------------------------------------
1464
1469 template <typename T>
1470 decltype(auto) set(uint16_t row) {
1471 verify_comp<T>();
1472
1473 GAIA_ASSERT2(
1474 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1475 "Set providing a row can only be used with generic components");
1476
1477 // Update the world version
1478 ::gaia::ecs::update_version(m_header.worldVersion);
1479
1480 GAIA_ASSERT(row < m_header.capacity);
1481 world_notify_on_set(*const_cast<World*>(m_header.world), comp_entity<T>(), *this, row, (uint16_t)(row + 1));
1482 return view_mut<T>()[row];
1483 }
1484
1489 template <typename T>
1490 decltype(auto) set_idx(uint16_t row, uint32_t compIdx) {
1491 verify_comp<T>();
1492
1493 GAIA_ASSERT2(
1494 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1495 "Set providing a row can only be used with generic components");
1496
1497 // Update the world version
1498 ::gaia::ecs::update_version(m_header.worldVersion);
1499
1500 world_notify_on_set(
1501 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, row, (uint16_t)(row + 1));
1502 return comp_mut_idx<T, true>(row, compIdx);
1503 }
1504
1508 template <typename T>
1509 decltype(auto) set_idx(uint32_t compIdx) {
1510 verify_comp<T>();
1511 static_assert(
1512 entity_kind_v<T> != EntityKind::EK_Gen,
1513 "Set not providing a row can only be used with non-generic components");
1514
1515 // Update the world version
1516 ::gaia::ecs::update_version(m_header.worldVersion);
1517
1518 world_notify_on_set(*const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx], *this, 0, 1);
1519 return comp_mut_idx<T, true>(0, compIdx);
1520 }
1521
1526 template <typename T>
1527 decltype(auto) set(uint16_t row, Entity type) {
1528 GAIA_ASSERT2(
1529 type.kind() == EntityKind::EK_Gen || row == 0,
1530 "Set providing a row can only be used with generic components");
1531 GAIA_ASSERT(type.kind() == entity_kind_v<T>);
1532 const uint32_t compIdx = comp_idx(type);
1533
1534 // Update the world version
1535 ::gaia::ecs::update_version(m_header.worldVersion);
1536
1537 GAIA_ASSERT(row < m_header.capacity);
1538 world_notify_on_set(*const_cast<World*>(m_header.world), type, *this, row, (uint16_t)(row + 1));
1539 return comp_mut_idx<T, true>(row, compIdx);
1540 }
1541
1547 template <typename T>
1548 decltype(auto) sset(uint16_t row) {
1549 GAIA_ASSERT2(
1550 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1551 "Set providing a row can only be used with generic components");
1552
1553 GAIA_ASSERT(row < m_header.capacity);
1554 return sview_mut<T>()[row];
1555 }
1556
1559 template <typename T>
1560 decltype(auto) sset_idx(uint16_t row, uint32_t compIdx) {
1561 verify_comp<T>();
1562
1563 GAIA_ASSERT2(
1564 actual_type_t<T>::Kind == EntityKind::EK_Gen || row == 0,
1565 "Set providing a row can only be used with generic components");
1566
1567 return comp_mut_idx<T, false>(row, compIdx);
1568 }
1569
1572 template <typename T>
1573 decltype(auto) sset_idx(uint32_t compIdx) {
1574 verify_comp<T>();
1575 static_assert(
1576 entity_kind_v<T> != EntityKind::EK_Gen,
1577 "SSet not providing a row can only be used with non-generic components");
1578
1579 return comp_mut_idx<T, false>(0, compIdx);
1580 }
1581
1588 template <typename T>
1589 decltype(auto) sset(uint16_t row, Entity type) {
1590 static_assert(core::is_raw_v<T>);
1591
1592 GAIA_ASSERT2(
1593 type.kind() == EntityKind::EK_Gen || row == 0,
1594 "Set providing a row can only be used with generic components");
1595 GAIA_ASSERT(type.kind() == entity_kind_v<T>);
1596 const uint32_t compIdx = comp_idx(type);
1597
1598 GAIA_ASSERT(row < m_header.capacity);
1599 return comp_mut_idx<T, false>(row, compIdx);
1600 }
1601
1602 //----------------------------------------------------------------------
1603 // Read component data
1604 //----------------------------------------------------------------------
1605
1612 template <typename T>
1613 GAIA_NODISCARD decltype(auto) get(uint16_t row) const {
1614 static_assert(
1615 entity_kind_v<T> == EntityKind::EK_Gen, "Get providing a row can only be used with generic components");
1616
1617 return comp_inter<T>(row);
1618 }
1619
1624 template <typename T>
1625 GAIA_NODISCARD decltype(auto) get_idx(uint16_t row, uint32_t compIdx) const {
1626 static_assert(
1627 entity_kind_v<T> == EntityKind::EK_Gen, "Get providing a row can only be used with generic components");
1628
1629 return comp_inter_idx<T>(row, compIdx);
1630 }
1631
1637 template <typename T>
1638 GAIA_NODISCARD decltype(auto) get(uint16_t row, Entity type) const {
1639 GAIA_ASSERT2(
1640 type.kind() == EntityKind::EK_Gen || row == 0,
1641 "Get providing a row can only be used with generic components");
1642 GAIA_ASSERT(type.kind() == entity_kind_v<T>);
1643 GAIA_ASSERT(row < m_header.count);
1644 const uint32_t compIdx = comp_idx(type);
1645 return comp_inter_idx<T>(row, compIdx);
1646 }
1647
1652 template <typename T>
1653 GAIA_NODISCARD decltype(auto) get() const {
1654 static_assert(
1655 entity_kind_v<T> != EntityKind::EK_Gen,
1656 "Get not providing a row can only be used with non-generic components");
1657
1658 return comp_inter<T>(0);
1659 }
1660
1664 template <typename T>
1665 GAIA_NODISCARD decltype(auto) get_idx(uint32_t compIdx) const {
1666 static_assert(
1667 entity_kind_v<T> != EntityKind::EK_Gen,
1668 "Get not providing a row can only be used with non-generic components");
1669
1670 return comp_inter_idx<T>(0, compIdx);
1671 }
1672
1673 template <typename T>
1674 GAIA_NODISCARD Entity comp_entity() const {
1675 if constexpr (is_pair<T>::value) {
1676 const auto rel = m_header.cc->get<typename T::rel>().entity;
1677 const auto tgt = m_header.cc->get<typename T::tgt>().entity;
1678 return (Entity)Pair(rel, tgt);
1679 } else {
1680 return m_header.cc->get<T>().entity;
1681 }
1682 }
1683
1688 GAIA_NODISCARD uint32_t comp_idx(Entity entity) const {
1689 return ecs::comp_idx<ChunkHeader::MAX_COMPONENTS>(m_records.pCompEntities, entity);
1690 }
1691
1697 GAIA_NODISCARD uint32_t comp_idx(Entity entity, uint32_t offset) const {
1698 return ecs::comp_idx({m_records.pCompEntities + offset, m_header.count - offset}, entity);
1699 }
1700
1701 //----------------------------------------------------------------------
1702
1704 void set_idx(uint32_t value) {
1705 m_header.index = value;
1706 }
1707
1709 GAIA_NODISCARD uint32_t idx() const {
1710 return m_header.index;
1711 }
1712
1714 GAIA_NODISCARD bool has_enabled_entities() const {
1715 return m_header.has_enabled_entities();
1716 }
1717
1719 GAIA_NODISCARD bool has_disabled_entities() const {
1720 return m_header.has_disabled_entities();
1721 }
1722
1724 GAIA_NODISCARD bool dying() const {
1725 return m_header.lifespanCountdown > 0;
1726 }
1727
1729 GAIA_NODISCARD bool queued_for_deletion() const {
1730 return m_header.deleteQueueIndex != BadIndex;
1731 }
1732
1734 GAIA_NODISCARD uint32_t delete_queue_index() const {
1735 return m_header.deleteQueueIndex;
1736 }
1737
1739 void delete_queue_index(uint32_t idx) {
1740 m_header.deleteQueueIndex = idx;
1741 }
1742
1745 m_header.deleteQueueIndex = BadIndex;
1746 }
1747
1749 void die() {
1750 m_header.dead = 1;
1751 }
1752
1754 GAIA_NODISCARD bool dead() const {
1755 return m_header.dead == 1;
1756 }
1757
1760 GAIA_ASSERT(!dead());
1761 GAIA_ASSERT(!queued_for_deletion());
1762 m_header.lifespanCountdown = ChunkHeader::MAX_CHUNK_LIFESPAN;
1763 }
1764
1766 void revive() {
1767 GAIA_ASSERT(!dead());
1768 m_header.lifespanCountdown = 0;
1769 clear_delete_queue_index();
1770 }
1771
1775 GAIA_ASSERT(dying());
1776 --m_header.lifespanCountdown;
1777 return dying();
1778 }
1779
1781 GAIA_NODISCARD bool full() const {
1782 return m_header.count >= m_header.capacity;
1783 }
1784
1786 GAIA_NODISCARD bool is_semi() const {
1787 // We want the chunk filled to at least 75% before considering it semi-full
1788 constexpr float Threshold = 0.75f;
1789 return ((float)m_header.count / (float)m_header.capacity) < Threshold;
1790 }
1791
1793 GAIA_NODISCARD uint16_t size() const {
1794 return m_header.count;
1795 }
1796
1798 GAIA_NODISCARD bool empty() const {
1799 return m_header.count == 0;
1800 }
1801
1803 GAIA_NODISCARD uint16_t size_enabled() const {
1804 return m_header.countEnabled;
1805 }
1806
1808 GAIA_NODISCARD uint16_t size_disabled() const {
1809 return (uint16_t)m_header.rowFirstEnabledEntity;
1810 }
1811
1813 GAIA_NODISCARD uint16_t capacity() const {
1814 return m_header.capacity;
1815 }
1816
1818 GAIA_NODISCARD uint8_t size_generic() const {
1819 return m_header.genEntities;
1820 }
1821
1825 GAIA_NODISCARD bool changed(uint32_t requiredVersion) const {
1826 const auto* versions = m_records.pVersions;
1827 const auto changeVersion = versions[0];
1828 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1829 }
1830
1832 GAIA_NODISCARD bool changed(uint32_t requiredVersion, uint32_t compIdx) const {
1833 const auto* versions = m_records.pVersions;
1834 // Do +1 because index 0 is reserved for the entity version number.
1835 const auto changeVersion = versions[compIdx + 1];
1836 return ::gaia::ecs::version_changed(changeVersion, requiredVersion);
1837 }
1838
1841 GAIA_NODISCARD bool entity_order_changed(uint32_t requiredVersion) const {
1842 return ::gaia::ecs::version_changed(m_header.entityOrderVersion, requiredVersion);
1843 }
1844
1846 GAIA_FORCEINLINE void update_world_version(uint32_t compIdx) {
1847 auto versions = comp_version_view_mut();
1848 // Automatically treat the entity as changed.
1849 versions[0] = m_header.worldVersion;
1850 // Do +1 because index 0 is reserved for the entity version number.
1851 versions[compIdx + 1] = m_header.worldVersion;
1852 // Sorted queries keyed by this component can invalidate their cached order immediately.
1853 world_invalidate_sorted_queries_for_entity(
1854 *const_cast<World*>(m_header.world), m_records.pCompEntities[compIdx]);
1855 }
1856
1858 GAIA_FORCEINLINE void update_entity_order_version() {
1859 m_header.entityOrderVersion = m_header.worldVersion;
1860 // Row-order changes invalidate cached sorted slices regardless of sort key.
1861 world_invalidate_sorted_queries(*const_cast<World*>(m_header.world));
1862 }
1863
1865 GAIA_FORCEINLINE void update_world_version() {
1866 // Edit the version pointer directly. The first elements is always the entity version.
1867 // This area of memory is always present.
1868 auto* versions = m_records.pVersions;
1869 // We update the version of the entity only. If this one changes,
1870 // all other components are considered changed as well.
1871 versions[0] = m_header.worldVersion;
1872 }
1873
1875 GAIA_FORCEINLINE void update_world_version_init() {
1876 auto* versions = m_records.pVersions;
1877 // We update the version of the entity and all components to match the world version.
1878 versions[0] = m_header.worldVersion;
1879 GAIA_FOR(m_header.genEntities) versions[1 + i] = m_header.worldVersion;
1880 m_header.entityOrderVersion = m_header.worldVersion;
1881 }
1882
1883 void diag() const {
1884 GAIA_LOG_N(
1885 " Chunk #%04u, entities:%u/%u, lifespanCountdown:%u", m_header.index, m_header.count, m_header.capacity,
1886 m_header.lifespanCountdown);
1887 }
1888 };
1889 } // namespace ecs
1890} // namespace gaia
Array of elements of type.
Definition sarray_ext_impl.h:27
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:782
GAIA_NODISCARD uint16_t capacity() const
Returns the number of entities in the chunk.
Definition chunk.h:1813
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:1665
static void free(Chunk *pChunk)
Releases all memory allocated by pChunk.
Definition chunk.h:490
const uint8_t & data(uint32_t offset) const
Returns an immutable pointer to chunk data.
Definition chunk.h:1369
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:1490
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:1589
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:1193
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:886
GAIA_FORCEINLINE void update_world_version()
Update the version of all components.
Definition chunk.h:1865
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:1697
bool progress_death()
Updates internal lifespan.
Definition chunk.h:1774
decltype(auto) set(uint16_t row)
Sets the value of the unique component T on row in the chunk.
Definition chunk.h:1470
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:334
void die()
Marks the chunk as dead (ready to delete)
Definition chunk.h:1749
GAIA_NODISCARD uint32_t idx() const
Returns the index of this chunk in its archetype's storage.
Definition chunk.h:1709
uint8_t & data(uint32_t offset)
Returns a mutable pointer to chunk data.
Definition chunk.h:1362
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:1560
void update_versions()
Updates the version numbers for this chunk.
Definition chunk.h:596
GAIA_FORCEINLINE void update_world_version(uint32_t compIdx)
Update the version of a component at the index.
Definition chunk.h:1846
GAIA_NODISCARD bool has_disabled_entities() const
Checks is this chunk has any disabled entities.
Definition chunk.h:1719
GAIA_NODISCARD bool full() const
Checks is the full capacity of the has has been reached.
Definition chunk.h:1781
GAIA_NODISCARD bool is_semi() const
Checks is the chunk is semi-full.
Definition chunk.h:1786
GAIA_NODISCARD decltype(auto) view(uint16_t from, uint16_t to) const
Returns a read-only entity or component view.
Definition chunk.h:609
GAIA_NODISCARD uint16_t add_entity(Entity entity)
Make.
Definition chunk.h:837
GAIA_NODISCARD uint16_t size_enabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1803
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:760
void start_dying()
Starts the process of dying (not yet ready to delete, can be revived)
Definition chunk.h:1759
GAIA_NODISCARD bool has() const
Checks if component T is present in the chunk.
Definition chunk.h:1450
GAIA_NODISCARD uint16_t size() const
Returns the total number of entities in the chunk (both enabled and disabled)
Definition chunk.h:1793
GAIA_FORCEINLINE void update_entity_order_version()
Updates the entity-order version after rows were added, removed, or reordered.
Definition chunk.h:1858
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:1613
GAIA_NODISCARD uint32_t delete_queue_index() const
Returns the index inside World's deferred chunk-delete queue.
Definition chunk.h:1734
static uintptr_t chunk_data_area_offset()
Returns the relative offset of m_data in Chunk.
Definition chunk.h:448
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:977
decltype(auto) set_idx(uint32_t compIdx)
Sets the value of a unique component using a pre-resolved component column.
Definition chunk.h:1509
void set_idx(uint32_t value)
Sets the index of this chunk in its archetype's storage.
Definition chunk.h:1704
void clear_delete_queue_index()
Clears the deferred chunk-delete queue index.
Definition chunk.h:1744
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:1841
GAIA_NODISCARD decltype(auto) get() const
Returns the value stored in the unique component T.
Definition chunk.h:1653
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:1832
void delete_queue_index(uint32_t idx)
Stores the index inside World's deferred chunk-delete queue.
Definition chunk.h:1739
decltype(auto) sset_idx(uint32_t compIdx)
Sets the value of a unique component using a pre-resolved component column.
Definition chunk.h:1573
static void copy_entity_data(Entity srcEntity, Entity dstEntity, EntityContainers &recs)
Copies all data associated with srcEntity into dstEntity.
Definition chunk.h:853
bool enabled(uint16_t row) const
Checks if the entity is enabled.
Definition chunk.h:1353
GAIA_NODISCARD bool dying() const
Checks is this chunk is dying.
Definition chunk.h:1724
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:1825
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:583
GAIA_NODISCARD uint16_t size_disabled() const
Return the number of entities in the chunk which are enabled.
Definition chunk.h:1808
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:1227
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:1065
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:1320
GAIA_NODISCARD uint32_t comp_idx(Entity entity) const
Returns the internal index of a component based on the provided entity.
Definition chunk.h:1688
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:1271
GAIA_FORCEINLINE void update_world_version_init()
Update the version of all components on chunk init.
Definition chunk.h:1875
GAIA_NODISCARD bool dead() const
Checks is this chunk is dead (ready to delete)
Definition chunk.h:1754
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:1527
GAIA_NODISCARD bool has_enabled_entities() const
Checks is this chunk has any enabled entities.
Definition chunk.h:1714
void revive()
Makes a dying chunk alive again.
Definition chunk.h:1766
GAIA_NODISCARD bool has(Entity entity) const
Checks if a component/entity entity is present in the chunk.
Definition chunk.h:1441
GAIA_NODISCARD bool queued_for_deletion() const
Returns true when the chunk is currently queued for deferred deletion.
Definition chunk.h:1729
GAIA_NODISCARD bool empty() const
Checks is there are any entities in the chunk.
Definition chunk.h:1798
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:1002
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:1129
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 Component *comps, const ChunkDataOffset *compOffs)
Allocates memory for a new chunk.
Definition chunk.h:459
GAIA_NODISCARD decltype(auto) view_mut(uint16_t from, uint16_t to)
Returns a mutable entity or component view.
Definition chunk.h:637
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:351
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:669
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:1625
GAIA_NODISCARD uint8_t size_generic() const
Returns the total number of generic entities/components in the chunk.
Definition chunk.h:1818
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:1638
decltype(auto) sset(uint16_t row)
Sets the value of the unique component T on row in the chunk.
Definition chunk.h:1548
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:919
GAIA_FORCEINLINE void modify()
Marks the component.
Definition chunk.h:703
Cache for compile-time defined components.
Definition component_cache.h:25
GAIA_NODISCARD const ComponentCacheItem * find(detail::ComponentDescId compDescId) const noexcept
Searches for the component cache item given the compDescId.
Definition component_cache.h:355
GAIA_NODISCARD const ComponentCacheItem & get(detail::ComponentDescId compDescId) const noexcept
Returns the component cache item given the compDescId.
Definition component_cache.h:373
Definition world.h:88
Wrapper for two Entities forming a relationship pair.
Definition id.h:500
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition utility.h:967
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:25
Entity entity
Component entity.
Definition component_cache_item.h:75
Component comp
Unique component identifier.
Definition component_cache_item.h:77
FuncCtor * func_ctor
Function to call when the component needs to be constructed.
Definition component_cache_item.h:88
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:34
Definition entity_container.h:141
Definition id.h:241
Definition id.h:233
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