Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
component_cache_item.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstdint>
5#include <cstring>
6#include <type_traits>
7
8#include "gaia/cnt/darray.h"
9#include "gaia/cnt/map.h"
10#include "gaia/core/hashing_string.h"
11#include "gaia/ecs/component.h"
12#include "gaia/ecs/component_desc.h"
13#include "gaia/ecs/id.h"
14#include "gaia/mem/mem_alloc.h"
15#include "gaia/mem/mem_utils.h"
16#include "gaia/mem/smallblock_allocator.h"
17#include "gaia/ser/ser_common.h"
18#include "gaia/ser/ser_rt.h"
19#include "gaia/util/str.h"
20
22namespace gaia {
23 namespace ecs {
24 class World;
25 class Chunk;
26 class ComponentCache;
27 struct ComponentRecord;
28
30 class SymbolTable final {
31 using Key = core::StringLookupKey<256>;
32
33 cnt::darray<Key> m_values;
34 cnt::map<Key, SymbolId> m_ids;
35
36 public:
37 SymbolTable() = default;
38 SymbolTable(const SymbolTable&) = delete;
39 SymbolTable(SymbolTable&&) = delete;
40 SymbolTable& operator=(const SymbolTable&) = delete;
41 SymbolTable& operator=(SymbolTable&&) = delete;
42
43 ~SymbolTable() {
44 clear();
45 }
46
50 GAIA_NODISCARD SymbolId intern(util::str_view value) {
51 if (value.empty())
52 return {};
53
54 const Key lookup(value.data(), value.size(), 0);
55 const auto it = m_ids.find(lookup);
56 if (it != m_ids.end())
57 return it->second;
58
59 auto* data = mem::AllocHelper::alloc<char>(value.size() + 1);
60 memcpy(data, value.data(), value.size());
61 data[value.size()] = 0;
62 const Key owned(data, value.size(), 1);
63 const SymbolId id{(uint32_t)m_values.size()};
64 m_values.push_back(owned);
65 m_ids.emplace(owned, id);
66 return id;
67 }
68
72 GAIA_NODISCARD util::str_view view(SymbolId id) const noexcept {
73 if (!id.valid() || id.value >= m_values.size())
74 return {};
75 const auto& value = m_values[id.value];
76 return {value.str(), value.len()};
77 }
78
80 void clear() {
81 m_ids.clear();
82 for (const auto& value: m_values) {
83 if (value.str() != nullptr && value.owned())
84 mem::AllocHelper::free((void*)value.str());
85 }
86 m_values.clear();
87 }
88 };
89
95 struct GAIA_API ComponentCacheItem final {
96 GAIA_USE_SMALLBLOCK(ComponentCacheItem);
97 friend class ComponentCache;
98
100 static constexpr uint32_t MaxNameLength = 256;
101
103 using SymbolLookupKey = core::StringLookupKey<512>;
104
106 using FuncCtor = void(void*, uint32_t);
108 using FuncDtor = void(void*, uint32_t);
110 using FuncFrom = void(void*, void*, uint32_t, uint32_t, uint32_t, uint32_t);
112 using FuncCopy = void(void*, const void*, uint32_t, uint32_t, uint32_t, uint32_t);
114 using FuncMove = void(void*, void*, uint32_t, uint32_t, uint32_t, uint32_t);
116 using FuncSwap = void(void*, void*, uint32_t, uint32_t, uint32_t, uint32_t);
118 using FuncCmp = bool(const void*, const void*);
120 using FuncCreateSparseStore = void(World&, Entity);
121
123 using FuncSave = void(ser::serializer&, const void*, uint32_t, uint32_t, uint32_t);
125 using FuncLoad = void(ser::serializer&, void*, uint32_t, uint32_t, uint32_t);
126
128 using FuncOnAdd = void(const World& world, const ComponentCacheItem&, Entity);
130 using FuncOnDel = void(const World& world, const ComponentCacheItem&, Entity);
132 using FuncOnSet = void(const World& world, const ComponentRecord&, Chunk& chunk);
133
135 Entity entity;
137 Component comp;
139 ComponentLookupHash hashLookup;
141 uint8_t soaSizes[meta::StructToTupleMaxTypes];
142
144 SymbolId name;
146 util::str path;
148 FuncCtor* func_ctor{};
150 FuncMove* func_move_ctor{};
152 FuncCopy* func_copy_ctor{};
154 FuncDtor* func_dtor{};
156 FuncCopy* func_copy{};
158 FuncMove* func_move{};
160 FuncSwap* func_swap{};
162 FuncCmp* func_cmp{};
164 mutable FuncCreateSparseStore* func_create_sparse_store{};
166 FuncSave* func_save{};
168 FuncLoad* func_load{};
170 RuntimeTypeKind typeKind = RuntimeTypeKind::Struct;
172 Entity semantic = EntityBad;
173#if GAIA_JSON_ENABLED
175 RuntimeJsonEncoding jsonEncoding = RuntimeJsonEncoding::Default;
176#endif
178 Entity underlyingType = EntityBad;
180 Entity elementType = EntityBad;
182 uint32_t elementCount = 0;
184 Entity opaqueAsType = EntityBad;
186 const RuntimeSequenceAdapter* sequenceAdapter = nullptr;
188 const RuntimeOpaqueAdapter* opaqueAdapter = nullptr;
189
190#if GAIA_ENABLE_HOOKS
192 struct Hooks {
193 #if GAIA_ENABLE_ADD_DEL_HOOKS
195 FuncOnAdd* func_add{};
197 FuncOnDel* func_del{};
198 #endif
199 #if GAIA_ENABLE_SET_HOOKS
201 FuncOnSet* func_set{};
202 #endif
203 };
205 Hooks comp_hooks;
206#endif
207
208 private:
210 const ComponentCache* m_ownerCache = nullptr;
212 SymbolTable* m_symbols = nullptr;
214 SymbolTable* m_ownedSymbols = nullptr;
216 cnt::darray<RuntimeFieldDesc> m_fields;
218 cnt::darray<RuntimeConstantDesc> m_constants;
219
223 GAIA_NODISCARD uint32_t soa_capacity(uint32_t capacity) const noexcept {
224 return entity.kind() == EntityKind::EK_Uni ? 1U : capacity;
225 }
226
234 void move_soa_element(
235 void* pDst, const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t capDst,
236 uint32_t capSrc) const noexcept {
237 capDst = soa_capacity(capDst);
238 capSrc = soa_capacity(capSrc);
239 const std::span<const uint8_t> fieldSizes{soaSizes, comp.soa()};
240 GAIA_FOR(comp.soa()) {
241 auto* pD = mem::data_view_policy_soa_erased::set(pDst, comp.alig(), fieldSizes, i, idxDst, capDst);
242 const auto* pS = mem::data_view_policy_soa_erased::get(pSrc, comp.alig(), fieldSizes, i, idxSrc, capSrc);
243 memmove(pD, pS, soaSizes[i]);
244 }
245 }
246
254 void swap_soa_elements(
255 void* pLeft, void* pRight, uint32_t idxLeft, uint32_t idxRight, uint32_t capLeft,
256 uint32_t capRight) const noexcept {
257 capLeft = soa_capacity(capLeft);
258 capRight = soa_capacity(capRight);
259 const std::span<const uint8_t> fieldSizes{soaSizes, comp.soa()};
260 GAIA_FOR(comp.soa()) {
261 auto* pL = mem::data_view_policy_soa_erased::set(pLeft, comp.alig(), fieldSizes, i, idxLeft, capLeft);
262 auto* pR = mem::data_view_policy_soa_erased::set(pRight, comp.alig(), fieldSizes, i, idxRight, capRight);
263 GAIA_FOR_(soaSizes[i], j) {
264 const auto value = pL[j];
265 pL[j] = pR[j];
266 pR[j] = value;
267 }
268 }
269 }
270
271 ComponentCacheItem() = default;
272 ~ComponentCacheItem() = default;
273
274 public:
276 ComponentCacheItem(const ComponentCacheItem&) = delete;
278 ComponentCacheItem(ComponentCacheItem&&) = delete;
280 ComponentCacheItem& operator=(const ComponentCacheItem&) = delete;
282 ComponentCacheItem& operator=(ComponentCacheItem&&) = delete;
283
286 GAIA_NODISCARD util::str_view symbol_name() const noexcept {
287 return m_symbols != nullptr ? m_symbols->view(name) : util::str_view{};
288 }
289
297 void
298 ctor_move(void* pDst, void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
299 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
300 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
301 if (func_move_ctor != nullptr) {
302 func_move_ctor(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
303 return;
304 }
305
306 if (comp.soa() != 0) {
307 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
308 return;
309 }
310 if (comp.size() == 0)
311 return;
312
313 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
314 auto* pS = (uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
315 memmove((void*)pD, (const void*)pS, comp.size());
316 }
317
325 void ctor_copy(
326 void* pDst, const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
327 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
328 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
329 if (func_copy_ctor != nullptr) {
330 func_copy_ctor(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
331 return;
332 }
333
334 if (comp.soa() != 0) {
335 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
336 return;
337 }
338 if (comp.size() == 0)
339 return;
340
341 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
342 auto* pS = (const uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
343 memcpy((void*)pD, (const void*)pS, comp.size());
344 }
345
348 void dtor(void* pSrc) const {
349 if (func_dtor != nullptr)
350 func_dtor(pSrc, 1);
351 }
352
360 void
361 copy(void* pDst, const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
362 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
363 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
364 if (func_copy != nullptr) {
365 func_copy(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
366 return;
367 }
368
369 if (comp.soa() != 0) {
370 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
371 return;
372 }
373 if (comp.size() == 0)
374 return;
375
376 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
377 auto* pS = (const uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
378 memcpy((void*)pD, (const void*)pS, comp.size());
379 }
380
388 void move(void* pDst, void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
389 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
390 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
391 if (func_move != nullptr) {
392 func_move(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
393 return;
394 }
395
396 if (comp.soa() != 0) {
397 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
398 return;
399 }
400 if (comp.size() == 0)
401 return;
402
403 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
404 auto* pS = (uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
405 memmove((void*)pD, (const void*)pS, comp.size());
406 }
407
415 void
416 swap(void* pLeft, void* pRight, uint32_t idxLeft, uint32_t idxRight, uint32_t sizeDst, uint32_t sizeSrc) const {
417 GAIA_ASSERT(pLeft != nullptr && pRight != nullptr);
418 if (func_swap != nullptr) {
419 func_swap(pLeft, pRight, idxLeft, idxRight, sizeDst, sizeSrc);
420 return;
421 }
422
423 if (comp.soa() != 0) {
424 swap_soa_elements(pLeft, pRight, idxLeft, idxRight, sizeDst, sizeSrc);
425 return;
426 }
427 if (comp.size() == 0)
428 return;
429
430 auto* l = (uint8_t*)pLeft + ((uintptr_t)comp.size() * idxLeft);
431 auto* r = (uint8_t*)pRight + ((uintptr_t)comp.size() * idxRight);
432 GAIA_FOR(comp.size()) {
433 const auto tmp = l[i];
434 l[i] = r[i];
435 r[i] = tmp;
436 }
437 }
438
443 bool cmp(const void* pLeft, const void* pRight) const {
444 GAIA_ASSERT(pLeft != pRight);
445 if (func_cmp != nullptr)
446 return func_cmp(pLeft, pRight);
447
448 if (comp.soa() != 0 || comp.size() == 0)
449 return true;
450
451 return memcmp(pLeft, pRight, comp.size()) == 0;
452 }
453
460 void save(ser::serializer& serializer, const void* pSrc, uint32_t from, uint32_t to, uint32_t cap) const {
461 GAIA_ASSERT(serializer.valid() && pSrc != nullptr && from < to && to <= cap);
462 if (func_save != nullptr) {
463 func_save(serializer, pSrc, from, to, cap);
464 return;
465 }
466
467 if (comp.soa() != 0 || comp.size() == 0)
468 return;
469
470 const auto* pBase = (const uint8_t*)pSrc;
471 GAIA_FOR2(from, to) {
472 const auto* p = pBase + ((uintptr_t)comp.size() * i);
473 serializer.save_raw((const void*)p, comp.size(), ser::serialization_type_id::trivial_wrapper);
474 }
475 }
476
483 void load(ser::serializer& serializer, void* pDst, uint32_t from, uint32_t to, uint32_t cap) const {
484 GAIA_ASSERT(serializer.valid() && pDst != nullptr && from < to && to <= cap);
485 if (func_load != nullptr) {
486 func_load(serializer, pDst, from, to, cap);
487 return;
488 }
489
490 if (comp.soa() != 0 || comp.size() == 0)
491 return;
492
493 auto* pBase = (uint8_t*)pDst;
494 GAIA_FOR2(from, to) {
495 auto* p = pBase + ((uintptr_t)comp.size() * i);
496 serializer.load_raw((void*)p, comp.size(), ser::serialization_type_id::trivial_wrapper);
497 }
498 }
499
501 GAIA_NODISCARD bool has_fields() const {
502 return !m_fields.empty();
503 }
504
509 template <typename StringType>
510 GAIA_NODISCARD static uint32_t field_element_count(const RuntimeFieldMeta<StringType>& field) noexcept {
511 return field.count == 0 ? 1U : field.count;
512 }
513
517 GAIA_NODISCARD static uint32_t primitive_type_size(Entity type) noexcept {
518 ser::serialization_type_id id = ser::serialization_type_id::ignore;
519 if (!runtime_primitive_serialization_type(type, id))
520 return 0;
521 return ser::serialization_type_size(id, 0);
522 }
523
527 GAIA_NODISCARD const RuntimeFieldDesc* field(uint32_t index) const noexcept {
528 return index < m_fields.size() ? &m_fields[index] : nullptr;
529 }
530
534 GAIA_NODISCARD const RuntimeFieldDesc* field(util::str_view fieldName) const noexcept {
535 if (fieldName.empty() || fieldName.size() >= MaxNameLength)
536 return nullptr;
537
538 for (const auto& field: m_fields) {
539 if (field_name(field) == fieldName)
540 return &field;
541 }
542 return nullptr;
543 }
544
548 GAIA_NODISCARD util::str_view field_name(const RuntimeFieldDesc& field) const noexcept {
549 return m_symbols != nullptr ? m_symbols->view(field.name) : util::str_view{};
550 }
551
555 GAIA_NODISCARD util::str_view field_unit(const RuntimeFieldDesc& field) const noexcept {
556 return m_symbols != nullptr ? m_symbols->view(field.unit) : util::str_view{};
557 }
558
560 GAIA_NODISCARD uint32_t field_count() const noexcept {
561 return (uint32_t)m_fields.size();
562 }
563
565 GAIA_NODISCARD const ComponentCache* owner_cache() const noexcept {
566 return m_ownerCache;
567 }
568
570 GAIA_NODISCARD Entity primitive_type() const noexcept {
571 if (typeKind == RuntimeTypeKind::Primitive)
572 return entity;
573 if (typeKind == RuntimeTypeKind::Enum || typeKind == RuntimeTypeKind::Bitmask)
574 return underlyingType;
575 return EntityBad;
576 }
577
579 GAIA_NODISCARD Entity element_type() const noexcept {
580 return typeKind == RuntimeTypeKind::Array || typeKind == RuntimeTypeKind::Vector ? elementType : EntityBad;
581 }
582
584 GAIA_NODISCARD uint32_t element_count() const noexcept {
585 return typeKind == RuntimeTypeKind::Array ? elementCount : 0;
586 }
587
589 GAIA_NODISCARD Entity opaque_as_type() const noexcept {
590 return typeKind == RuntimeTypeKind::Opaque ? opaqueAsType : EntityBad;
591 }
592
594 GAIA_NODISCARD const RuntimeSequenceAdapter* sequence_adapter() const noexcept {
595 return typeKind == RuntimeTypeKind::Vector ? sequenceAdapter : nullptr;
596 }
597
599 GAIA_NODISCARD const RuntimeOpaqueAdapter* opaque_adapter() const noexcept {
600 return typeKind == RuntimeTypeKind::Opaque ? opaqueAdapter : nullptr;
601 }
602
604 GAIA_NODISCARD bool has_custom_serializer() const noexcept {
605 return func_save != nullptr;
606 }
607
609 GAIA_NODISCARD bool has_custom_deserializer() const noexcept {
610 return func_load != nullptr;
611 }
612
616 GAIA_NODISCARD const RuntimeConstantDesc* constant(uint32_t index) const noexcept {
617 return index < m_constants.size() ? &m_constants[index] : nullptr;
618 }
619
623 GAIA_NODISCARD const RuntimeConstantDesc* constant(util::str_view constantName) const noexcept {
624 if (constantName.empty() || constantName.size() >= MaxNameLength)
625 return nullptr;
626
627 for (const auto& constant: m_constants) {
628 if (constant_name(constant) == constantName)
629 return &constant;
630 }
631 return nullptr;
632 }
633
637 GAIA_NODISCARD util::str_view constant_name(const RuntimeConstantDesc& constant) const noexcept {
638 return m_symbols != nullptr ? m_symbols->view(constant.name) : util::str_view{};
639 }
640
644 GAIA_NODISCARD const RuntimeConstantDesc* constant_by_value(int64_t value) const noexcept {
645 for (const auto& constant: m_constants) {
646 if (constant.value == value)
647 return &constant;
648 }
649 return nullptr;
650 }
651
653 GAIA_NODISCARD uint32_t constant_count() const noexcept {
654 return (uint32_t)m_constants.size();
655 }
656
657#if GAIA_ENABLE_HOOKS
659 Hooks& hooks() {
660 return comp_hooks;
661 }
662
664 const Hooks& hooks() const {
665 return comp_hooks;
666 }
667
668#endif
669
674 GAIA_NODISCARD uint32_t calc_new_mem_offset(uint32_t addr, size_t cnt) const noexcept {
675 if (comp.soa() == 0) {
676 addr = (uint32_t)mem::detail::get_aligned_byte_offset(addr, comp.alig(), comp.size(), cnt);
677 } else {
678 GAIA_FOR(comp.soa()) {
679 addr = (uint32_t)mem::detail::get_aligned_byte_offset(addr, comp.alig(), soaSizes[i], cnt);
680 }
681 // TODO: Magic offset. Otherwise, SoA data might leak past the chunk boundary when accessing
682 // the last element. By faking the memory offset we can bypass this is issue for now.
683 // Obviously, this needs fixing at some point.
684 addr += comp.soa() * 12;
685 }
686 return addr;
687 }
688
689 private:
694 template <typename T>
695 GAIA_NODISCARD static uint32_t init_type_name(char (&nameTmp)[MaxNameLength]) {
696 auto ct_name = detail::ComponentDesc<T>::name();
697
698 // Allocate enough memory for the name string + the null-terminating character (
699 // the compile time string returned by ComponentDesc<T>::name is not null-terminated).
700 // Different compilers will give a bit different strings, e.g.:
701 // Clang/GCC: gaia::ecs::uni<Position>
702 // MSVC : gaia::ecs::uni<struct Position>
703 // Therefore, we first copy the compile-time string and then tweak it so it is
704 // the same on all supported compilers.
705 auto nameTmpLen = (uint32_t)ct_name.size();
706 GAIA_ASSERT(nameTmpLen < MaxNameLength);
707 memcpy((void*)nameTmp, (const void*)ct_name.data(), nameTmpLen);
708 nameTmp[ct_name.size()] = 0;
709
710 auto strip_prefix = [&](const char* prefix, uint32_t prefixLen) {
711 if (nameTmpLen <= prefixLen || strncmp(nameTmp, prefix, prefixLen) != 0)
712 return;
713
714 memmove(nameTmp, nameTmp + prefixLen, nameTmpLen - prefixLen + 1);
715 nameTmpLen -= prefixLen;
716 };
717
718 strip_prefix("const ", 6);
719
720 const uint32_t NSubstrings = 3;
721 const char* to_remove[NSubstrings] = {"class ", "struct ", "enum "};
722 const uint32_t to_remove_len[NSubstrings] = {6, 7, 5};
723 GAIA_FOR(NSubstrings) {
724 strip_prefix(to_remove[i], to_remove_len[i]);
725 }
726
727 while (nameTmpLen > 0) {
728 const auto ch = nameTmp[nameTmpLen - 1];
729 if (ch != ' ' && ch != '&' && ch != '*')
730 break;
731
732 nameTmp[--nameTmpLen] = 0;
733 }
734
735 if (nameTmpLen > 6 && strncmp(nameTmp + nameTmpLen - 6, " const", 6) == 0) {
736 nameTmpLen -= 6;
737 nameTmp[nameTmpLen] = 0;
738 }
739
740 // Normalization template names by removing keywords when they appear as template argument
741 // prefixes instead of as part of a longer identifier.
742 GAIA_FOR(NSubstrings) {
743 const auto* str = to_remove[i];
744 const auto len = to_remove_len[i];
745
746 auto* pos = nameTmp;
747 while ((pos = strstr(pos, str)) != nullptr) {
748 const bool isBoundary = pos == nameTmp || pos[-1] == '<' || pos[-1] == ',' || pos[-1] == ' ';
749 if (!isBoundary) {
750 ++pos;
751 continue;
752 }
753
754 const auto tailMaxLen = (size_t)(MaxNameLength - (uint32_t)(pos + len - nameTmp));
755 memmove(pos, pos + len, GAIA_STRLEN(pos + len, tailMaxLen) + 1);
756 nameTmpLen -= len;
757 }
758 }
759
760 return nameTmpLen;
761 }
762
766 GAIA_NODISCARD bool copy_runtime_field(const RuntimeFieldInit& desc) {
767 if (m_symbols == nullptr || desc.name.empty() || desc.name.size() >= MaxNameLength ||
768 desc.unit.size() >= RuntimeFieldDesc::MaxUnitLength)
769 return false;
770
771 RuntimeFieldDesc field{};
772 field.name = m_symbols->intern(desc.name);
773 field.type = desc.type;
774 field.offset = desc.offset;
775 field.count = desc.count;
776 field.semantic = desc.semantic;
777#if GAIA_JSON_ENABLED
778 field.jsonEncoding = desc.jsonEncoding;
779#endif
780 field.flags = desc.flags;
781 field.unit = m_symbols->intern(desc.unit);
782 field.minimum = desc.minimum;
783 field.maximum = desc.maximum;
784 field.step = desc.step;
785 m_fields.push_back(field);
786 return true;
787 }
788
792 GAIA_NODISCARD bool copy_runtime_constant(const RuntimeConstantInit& desc) {
793 if (m_symbols == nullptr || desc.name.empty() || desc.name.size() >= MaxNameLength)
794 return false;
795
796 RuntimeConstantDesc constant{};
797 constant.name = m_symbols->intern(desc.name);
798 constant.value = desc.value;
799 m_constants.push_back(constant);
800 return true;
801 }
802
803 template <typename T>
804 GAIA_NODISCARD static ComponentCacheItem*
805 create_typed(Entity entity, SymbolTable& symbols, const RuntimeTypeDesc* pRuntimeType) {
806 static_assert(core::is_raw_v<T>);
807
808 constexpr auto componentSize = detail::ComponentDesc<T>::size();
809 static_assert(
810 componentSize < Component::MaxComponentSizeInBytes,
811 "Trying to register a component larger than the maximum allowed component size! In the future this "
812 "restriction won't apply to components not stored inside archetype chunks.");
813
814 char nameTmp[MaxNameLength];
815 const auto nameTmpLen = init_type_name<T>(nameTmp);
816
817 uint8_t soaSizes[meta::StructToTupleMaxTypes]{};
818 RuntimeFieldInit fields[meta::StructToTupleMaxTypes]{};
819 auto desc = detail::ComponentDesc<T>::make(
820 util::str_view(nameTmp, nameTmpLen), std::span<uint8_t, meta::StructToTupleMaxTypes>{soaSizes});
821 if (pRuntimeType != nullptr) {
822 desc.runtimeType = *pRuntimeType;
823 }
824#if GAIA_ECS_AUTO_COMPONENT_FIELDS
825 else {
826 desc.runtimeType.fields = fields;
827 desc.runtimeType.fieldCount =
828 detail::ComponentDesc<T>::auto_fields(std::span<RuntimeFieldInit, meta::StructToTupleMaxTypes>{fields});
829 }
830#endif
831 return create(entity, symbols, desc);
832 }
833
834 public:
839 template <typename T>
840 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity) {
841 auto* symbols = new SymbolTable();
842 auto* item = create<T>(entity, *symbols);
843 item->m_ownedSymbols = symbols;
844 return item;
845 }
846
847 private:
852 template <typename T>
853 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, SymbolTable& symbols) {
854 return create_typed<T>(entity, symbols, nullptr);
855 }
856
857 public:
863 template <typename T>
864 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, const RuntimeTypeDesc& runtimeType) {
865 auto* symbols = new SymbolTable();
866 auto* item = create<T>(entity, *symbols, runtimeType);
867 item->m_ownedSymbols = symbols;
868 return item;
869 }
870
871 private:
877 template <typename T>
878 GAIA_NODISCARD static ComponentCacheItem*
879 create(Entity entity, SymbolTable& symbols, const RuntimeTypeDesc& runtimeType) {
880 return create_typed<T>(entity, symbols, &runtimeType);
881 }
882
887 GAIA_NODISCARD static ComponentCacheItem*
888 create(Entity entity, SymbolTable& symbols, const ecs::ComponentDesc& desc) {
889 GAIA_ASSERT(!desc.name.empty());
890 GAIA_ASSERT(desc.name.size() < MaxNameLength);
891 GAIA_ASSERT(desc.size < Component::MaxComponentSizeInBytes);
892 GAIA_ASSERT((desc.size == 0 && desc.alig == 0) || (desc.alig > 0 && desc.alig < Component::MaxAlignment));
893 GAIA_ASSERT(desc.alig == 0 || (desc.alig & (desc.alig - 1)) == 0);
894 GAIA_ASSERT(desc.soa <= meta::StructToTupleMaxTypes);
895 GAIA_ASSERT(desc.soa == 0 || desc.pSoaSizes != nullptr);
896#if GAIA_ASSERT_ENABLED
897 if (desc.soa > 0) {
898 uint32_t soaSize = 0;
899 GAIA_FOR(desc.soa) {
900 GAIA_ASSERT(desc.pSoaSizes[i] != 0);
901 soaSize += desc.pSoaSizes[i];
902 }
903 GAIA_ASSERT(soaSize <= desc.size);
904 }
905#endif
906
907 auto* cci = new ComponentCacheItem();
908 cci->m_symbols = &symbols;
909 cci->entity = entity;
910 cci->comp = Component(entity.id(), desc.soa, desc.size, desc.alig, desc.storageType);
911 cci->hashLookup = desc.hashLookup.hash != 0
912 ? desc.hashLookup
913 : ComponentLookupHash{core::calculate_hash64(desc.name.data(), desc.name.size())};
914
915 if (desc.soa > 0) {
916 GAIA_FOR(desc.soa) cci->soaSizes[i] = desc.pSoaSizes[i];
917 }
918
919 cci->name = symbols.intern(desc.name);
920 GAIA_ASSERT(cci->name.valid());
921
922 cci->func_ctor = desc.funcCtor;
923 cci->func_move_ctor = desc.funcMoveCtor;
924 cci->func_copy_ctor = desc.funcCopyCtor;
925 cci->func_dtor = desc.funcDtor;
926 cci->func_copy = desc.funcCopy;
927 cci->func_move = desc.funcMove;
928 cci->func_swap = desc.funcSwap;
929 cci->func_cmp = desc.funcCmp;
930 cci->func_save = desc.funcSave;
931 cci->func_load = desc.funcLoad;
932
933 const auto& runtimeType = desc.runtimeType;
934 cci->typeKind = runtimeType.typeKind;
935 cci->semantic = runtimeType.semantic;
936#if GAIA_JSON_ENABLED
937 cci->jsonEncoding = runtimeType.jsonEncoding;
938#endif
939 cci->underlyingType = runtimeType.underlyingType;
940 cci->elementType = runtimeType.elementType;
941 cci->elementCount = runtimeType.elementCount;
942 cci->opaqueAsType = runtimeType.opaqueAsType;
943 cci->sequenceAdapter = runtimeType.sequenceAdapter;
944 cci->opaqueAdapter = runtimeType.opaqueAdapter;
945
946 if (runtimeType.fieldCount > 0) {
947 GAIA_FOR(runtimeType.fieldCount) {
948 const bool copied = cci->copy_runtime_field(runtimeType.fields[i]);
949 GAIA_ASSERT(copied);
950 }
951 }
952
953 if (runtimeType.constantCount > 0) {
954 GAIA_FOR(runtimeType.constantCount) {
955 const bool copied = cci->copy_runtime_constant(runtimeType.constants[i]);
956 GAIA_ASSERT(copied);
957 }
958 }
959
960 return cci;
961 }
962
963 public:
968 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, const ecs::ComponentDesc& desc) {
969 auto* symbols = new SymbolTable();
970 auto* item = create(entity, *symbols, desc);
971 item->m_ownedSymbols = symbols;
972 return item;
973 }
974
977 static void destroy(ComponentCacheItem* pItem) {
978 if (pItem == nullptr)
979 return;
980
981 auto* ownedSymbols = pItem->m_ownedSymbols;
982 delete pItem;
983 delete ownedSymbols;
984 }
985 };
986 } // namespace ecs
987} // namespace gaia
void load(Reader &reader, T &data)
Read data using Reader at compile-time.
Definition ser_ct.h:112
void save(Writer &writer, const T &data)
Write data using Writer at compile-time.
Definition ser_ct.h:101