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 SymbolId description;
148 util::str path;
150 FuncCtor* func_ctor{};
152 FuncMove* func_move_ctor{};
154 FuncCopy* func_copy_ctor{};
156 FuncDtor* func_dtor{};
158 FuncCopy* func_copy{};
160 FuncMove* func_move{};
162 FuncSwap* func_swap{};
164 FuncCmp* func_cmp{};
166 mutable FuncCreateSparseStore* func_create_sparse_store{};
168 FuncSave* func_save{};
170 FuncLoad* func_load{};
172 RuntimeTypeKind typeKind = RuntimeTypeKind::Struct;
174 Entity semantic = EntityBad;
175#if GAIA_JSON_ENABLED
177 RuntimeJsonEncoding jsonEncoding = RuntimeJsonEncoding::Default;
178#endif
180 Entity underlyingType = EntityBad;
182 Entity elementType = EntityBad;
184 uint32_t elementCount = 0;
186 Entity opaqueAsType = EntityBad;
188 const RuntimeSequenceAdapter* sequenceAdapter = nullptr;
190 const RuntimeOpaqueAdapter* opaqueAdapter = nullptr;
191
192#if GAIA_ENABLE_HOOKS
194 struct Hooks {
195 #if GAIA_ENABLE_ADD_DEL_HOOKS
197 FuncOnAdd* func_add{};
199 FuncOnDel* func_del{};
200 #endif
201 #if GAIA_ENABLE_SET_HOOKS
203 FuncOnSet* func_set{};
204 #endif
205 };
207 Hooks comp_hooks;
208#endif
209
210 private:
211#if GAIA_ENABLE_HOOKS
213 bool* m_pHooksAccessed = nullptr;
214#endif
216 const ComponentCache* m_ownerCache = nullptr;
218 SymbolTable* m_symbols = nullptr;
220 SymbolTable* m_ownedSymbols = nullptr;
222 cnt::darray<RuntimeFieldDesc> m_fields;
224 cnt::darray<RuntimeConstantDesc> m_constants;
225
233 void move_soa_element(
234 void* pDst, const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t capDst,
235 uint32_t capSrc) const noexcept {
236 const std::span<const uint8_t> fieldSizes{soaSizes, comp.soa()};
237 GAIA_FOR(comp.soa()) {
238 auto* pD = mem::data_view_policy_soa_erased::set(pDst, comp.alig(), fieldSizes, i, idxDst, capDst);
239 const auto* pS = mem::data_view_policy_soa_erased::get(pSrc, comp.alig(), fieldSizes, i, idxSrc, capSrc);
240 memmove(pD, pS, soaSizes[i]);
241 }
242 }
243
251 void swap_soa_elements(
252 void* pLeft, void* pRight, uint32_t idxLeft, uint32_t idxRight, uint32_t capLeft,
253 uint32_t capRight) const noexcept {
254 const std::span<const uint8_t> fieldSizes{soaSizes, comp.soa()};
255 GAIA_FOR(comp.soa()) {
256 auto* pL = mem::data_view_policy_soa_erased::set(pLeft, comp.alig(), fieldSizes, i, idxLeft, capLeft);
257 auto* pR = mem::data_view_policy_soa_erased::set(pRight, comp.alig(), fieldSizes, i, idxRight, capRight);
258 GAIA_FOR_(soaSizes[i], j) {
259 const auto value = pL[j];
260 pL[j] = pR[j];
261 pR[j] = value;
262 }
263 }
264 }
265
266 ComponentCacheItem() = default;
267 ~ComponentCacheItem() = default;
268
269 public:
271 ComponentCacheItem(const ComponentCacheItem&) = delete;
273 ComponentCacheItem(ComponentCacheItem&&) = delete;
275 ComponentCacheItem& operator=(const ComponentCacheItem&) = delete;
277 ComponentCacheItem& operator=(ComponentCacheItem&&) = delete;
278
281 GAIA_NODISCARD util::str_view symbol_name() const noexcept {
282 return m_symbols != nullptr ? m_symbols->view(name) : util::str_view{};
283 }
284
292 void
293 ctor_move(void* pDst, void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
294 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
295 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
296 if (func_move_ctor != nullptr) {
297 func_move_ctor(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
298 return;
299 }
300
301 if (comp.soa() != 0) {
302 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
303 return;
304 }
305 if (comp.size() == 0)
306 return;
307
308 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
309 auto* pS = (uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
310 memmove((void*)pD, (const void*)pS, comp.size());
311 }
312
320 void ctor_copy(
321 void* pDst, const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
322 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
323 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
324 if (func_copy_ctor != nullptr) {
325 func_copy_ctor(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
326 return;
327 }
328
329 if (comp.soa() != 0) {
330 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
331 return;
332 }
333 if (comp.size() == 0)
334 return;
335
336 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
337 auto* pS = (const uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
338 memcpy((void*)pD, (const void*)pS, comp.size());
339 }
340
343 void dtor(void* pSrc) const {
344 if (func_dtor != nullptr)
345 func_dtor(pSrc, 1);
346 }
347
355 void
356 copy(void* pDst, const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
357 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
358 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
359 if (func_copy != nullptr) {
360 func_copy(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
361 return;
362 }
363
364 if (comp.soa() != 0) {
365 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
366 return;
367 }
368 if (comp.size() == 0)
369 return;
370
371 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
372 auto* pS = (const uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
373 memcpy((void*)pD, (const void*)pS, comp.size());
374 }
375
383 void move(void* pDst, void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc) const {
384 GAIA_ASSERT(pSrc != nullptr && pDst != nullptr);
385 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
386 if (func_move != nullptr) {
387 func_move(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
388 return;
389 }
390
391 if (comp.soa() != 0) {
392 move_soa_element(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
393 return;
394 }
395 if (comp.size() == 0)
396 return;
397
398 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
399 auto* pS = (uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
400 memmove((void*)pD, (const void*)pS, comp.size());
401 }
402
410 void
411 swap(void* pLeft, void* pRight, uint32_t idxLeft, uint32_t idxRight, uint32_t sizeDst, uint32_t sizeSrc) const {
412 GAIA_ASSERT(pLeft != nullptr && pRight != nullptr);
413 if (func_swap != nullptr) {
414 func_swap(pLeft, pRight, idxLeft, idxRight, sizeDst, sizeSrc);
415 return;
416 }
417
418 if (comp.soa() != 0) {
419 swap_soa_elements(pLeft, pRight, idxLeft, idxRight, sizeDst, sizeSrc);
420 return;
421 }
422 if (comp.size() == 0)
423 return;
424
425 auto* l = (uint8_t*)pLeft + ((uintptr_t)comp.size() * idxLeft);
426 auto* r = (uint8_t*)pRight + ((uintptr_t)comp.size() * idxRight);
427 GAIA_FOR(comp.size()) {
428 const auto tmp = l[i];
429 l[i] = r[i];
430 r[i] = tmp;
431 }
432 }
433
438 bool cmp(const void* pLeft, const void* pRight) const {
439 GAIA_ASSERT(pLeft != pRight);
440 if (func_cmp != nullptr)
441 return func_cmp(pLeft, pRight);
442
443 if (comp.soa() != 0 || comp.size() == 0)
444 return true;
445
446 return memcmp(pLeft, pRight, comp.size()) == 0;
447 }
448
455 void save(ser::serializer& serializer, const void* pSrc, uint32_t from, uint32_t to, uint32_t cap) const {
456 GAIA_ASSERT(serializer.valid() && pSrc != nullptr && from < to && to <= cap);
457 if (func_save != nullptr) {
458 func_save(serializer, pSrc, from, to, cap);
459 return;
460 }
461
462 if (comp.soa() != 0 || comp.size() == 0)
463 return;
464
465 const auto* pBase = (const uint8_t*)pSrc;
466 GAIA_FOR2(from, to) {
467 const auto* p = pBase + ((uintptr_t)comp.size() * i);
468 serializer.save_raw((const void*)p, comp.size(), ser::serialization_type_id::trivial_wrapper);
469 }
470 }
471
478 void load(ser::serializer& serializer, void* pDst, uint32_t from, uint32_t to, uint32_t cap) const {
479 GAIA_ASSERT(serializer.valid() && pDst != nullptr && from < to && to <= cap);
480 if (func_load != nullptr) {
481 func_load(serializer, pDst, from, to, cap);
482 return;
483 }
484
485 if (comp.soa() != 0 || comp.size() == 0)
486 return;
487
488 auto* pBase = (uint8_t*)pDst;
489 GAIA_FOR2(from, to) {
490 auto* p = pBase + ((uintptr_t)comp.size() * i);
491 serializer.load_raw((void*)p, comp.size(), ser::serialization_type_id::trivial_wrapper);
492 }
493 }
494
496 GAIA_NODISCARD bool has_fields() const {
497 return !m_fields.empty();
498 }
499
504 template <typename StringType>
505 GAIA_NODISCARD static uint32_t field_element_count(const RuntimeFieldMeta<StringType>& field) noexcept {
506 return field.count == 0 ? 1U : field.count;
507 }
508
512 GAIA_NODISCARD static uint32_t primitive_type_size(Entity type) noexcept {
513 ser::serialization_type_id id = ser::serialization_type_id::ignore;
514 if (!runtime_primitive_serialization_type(type, id))
515 return 0;
516 return ser::serialization_type_size(id, 0);
517 }
518
522 GAIA_NODISCARD const RuntimeFieldDesc* field(uint32_t index) const noexcept {
523 return index < m_fields.size() ? &m_fields[index] : nullptr;
524 }
525
529 GAIA_NODISCARD const RuntimeFieldDesc* field(util::str_view fieldName) const noexcept {
530 if (fieldName.empty() || fieldName.size() >= MaxNameLength)
531 return nullptr;
532
533 for (const auto& field: m_fields) {
534 if (field_name(field) == fieldName)
535 return &field;
536 }
537 return nullptr;
538 }
539
543 GAIA_NODISCARD util::str_view field_name(const RuntimeFieldDesc& field) const noexcept {
544 return m_symbols != nullptr ? m_symbols->view(field.name) : util::str_view{};
545 }
546
550 GAIA_NODISCARD util::str_view field_unit(const RuntimeFieldDesc& field) const noexcept {
551 return m_symbols != nullptr ? m_symbols->view(field.unit) : util::str_view{};
552 }
553
557 GAIA_NODISCARD util::str_view field_description(const RuntimeFieldDesc& field) const noexcept {
558 return m_symbols != nullptr ? m_symbols->view(field.description) : util::str_view{};
559 }
560
563 GAIA_NODISCARD util::str_view type_description() const noexcept {
564 return m_symbols != nullptr ? m_symbols->view(description) : util::str_view{};
565 }
566
568 GAIA_NODISCARD uint32_t field_count() const noexcept {
569 return (uint32_t)m_fields.size();
570 }
571
573 GAIA_NODISCARD const ComponentCache* owner_cache() const noexcept {
574 return m_ownerCache;
575 }
576
578 GAIA_NODISCARD Entity primitive_type() const noexcept {
579 if (typeKind == RuntimeTypeKind::Primitive)
580 return entity;
581 if (typeKind == RuntimeTypeKind::Enum || typeKind == RuntimeTypeKind::Bitmask)
582 return underlyingType;
583 return EntityBad;
584 }
585
587 GAIA_NODISCARD Entity element_type() const noexcept {
588 return typeKind == RuntimeTypeKind::Array || typeKind == RuntimeTypeKind::Vector ? elementType : EntityBad;
589 }
590
592 GAIA_NODISCARD uint32_t element_count() const noexcept {
593 return typeKind == RuntimeTypeKind::Array ? elementCount : 0;
594 }
595
597 GAIA_NODISCARD Entity opaque_as_type() const noexcept {
598 return typeKind == RuntimeTypeKind::Opaque ? opaqueAsType : EntityBad;
599 }
600
602 GAIA_NODISCARD const RuntimeSequenceAdapter* sequence_adapter() const noexcept {
603 return typeKind == RuntimeTypeKind::Vector ? sequenceAdapter : nullptr;
604 }
605
607 GAIA_NODISCARD const RuntimeOpaqueAdapter* opaque_adapter() const noexcept {
608 return typeKind == RuntimeTypeKind::Opaque ? opaqueAdapter : nullptr;
609 }
610
612 GAIA_NODISCARD bool has_custom_serializer() const noexcept {
613 return func_save != nullptr;
614 }
615
617 GAIA_NODISCARD bool has_custom_deserializer() const noexcept {
618 return func_load != nullptr;
619 }
620
624 GAIA_NODISCARD const RuntimeConstantDesc* constant(uint32_t index) const noexcept {
625 return index < m_constants.size() ? &m_constants[index] : nullptr;
626 }
627
631 GAIA_NODISCARD const RuntimeConstantDesc* constant(util::str_view constantName) const noexcept {
632 if (constantName.empty() || constantName.size() >= MaxNameLength)
633 return nullptr;
634
635 for (const auto& constant: m_constants) {
636 if (constant_name(constant) == constantName)
637 return &constant;
638 }
639 return nullptr;
640 }
641
645 GAIA_NODISCARD util::str_view constant_name(const RuntimeConstantDesc& constant) const noexcept {
646 return m_symbols != nullptr ? m_symbols->view(constant.name) : util::str_view{};
647 }
648
652 GAIA_NODISCARD util::str_view constant_description(const RuntimeConstantDesc& constant) const noexcept {
653 return m_symbols != nullptr ? m_symbols->view(constant.description) : util::str_view{};
654 }
655
659 GAIA_NODISCARD const RuntimeConstantDesc* constant_by_value(int64_t value) const noexcept {
660 for (const auto& constant: m_constants) {
661 if (constant.value == value)
662 return &constant;
663 }
664 return nullptr;
665 }
666
668 GAIA_NODISCARD uint32_t constant_count() const noexcept {
669 return (uint32_t)m_constants.size();
670 }
671
672#if GAIA_ENABLE_HOOKS
674 Hooks& hooks() {
675 if (m_pHooksAccessed != nullptr)
676 *m_pHooksAccessed = true;
677 return comp_hooks;
678 }
679
681 const Hooks& hooks() const {
682 return comp_hooks;
683 }
684
685#endif
686
691 GAIA_NODISCARD uint32_t calc_new_mem_offset(uint32_t addr, size_t cnt) const noexcept {
692 if (comp.soa() == 0) {
693 addr = (uint32_t)mem::detail::get_aligned_byte_offset(addr, comp.alig(), comp.size(), cnt);
694 } else {
695 GAIA_FOR(comp.soa()) {
696 addr = (uint32_t)mem::detail::get_aligned_byte_offset(addr, comp.alig(), soaSizes[i], cnt);
697 }
698 // TODO: Magic offset. Otherwise, SoA data might leak past the chunk boundary when accessing
699 // the last element. By faking the memory offset we can bypass this is issue for now.
700 // Obviously, this needs fixing at some point.
701 addr += comp.soa() * 12;
702 }
703 return addr;
704 }
705
706 private:
711 template <typename T>
712 GAIA_NODISCARD static uint32_t init_type_name(char (&nameTmp)[MaxNameLength]) {
713 auto ct_name = detail::ComponentDesc<T>::name();
714
715 // Allocate enough memory for the name string + the null-terminating character (
716 // the compile time string returned by ComponentDesc<T>::name is not null-terminated).
717 // Different compilers will give a bit different strings, e.g.:
718 // Clang/GCC: aaa::bbb::temp<Position>
719 // MSVC : aaa::bbb::temp<struct Position>
720 // Therefore, we first copy the compile-time string and then tweak it so it is
721 // the same on all supported compilers.
722 auto nameTmpLen = (uint32_t)ct_name.size();
723 GAIA_ASSERT(nameTmpLen < MaxNameLength);
724 memcpy((void*)nameTmp, (const void*)ct_name.data(), nameTmpLen);
725 nameTmp[ct_name.size()] = 0;
726
727 auto strip_prefix = [&](const char* prefix, uint32_t prefixLen) {
728 if (nameTmpLen <= prefixLen || memcmp(nameTmp, prefix, prefixLen) != 0)
729 return;
730
731 memmove(nameTmp, nameTmp + prefixLen, nameTmpLen - prefixLen + 1);
732 nameTmpLen -= prefixLen;
733 };
734
735 strip_prefix("const ", 6);
736
737 const uint32_t NSubstrings = 3;
738 const char* to_remove[NSubstrings] = {"class ", "struct ", "enum "};
739 const uint32_t to_remove_len[NSubstrings] = {6, 7, 5};
740 GAIA_FOR(NSubstrings) {
741 strip_prefix(to_remove[i], to_remove_len[i]);
742 }
743
744 while (nameTmpLen > 0) {
745 const auto ch = nameTmp[nameTmpLen - 1];
746 if (ch != ' ' && ch != '&' && ch != '*')
747 break;
748
749 nameTmp[--nameTmpLen] = 0;
750 }
751
752 if (nameTmpLen > 6 && strncmp(nameTmp + nameTmpLen - 6, " const", 6) == 0) {
753 nameTmpLen -= 6;
754 nameTmp[nameTmpLen] = 0;
755 }
756
757 // Normalization template names by removing keywords when they appear as template argument
758 // prefixes instead of as part of a longer identifier.
759 GAIA_FOR(NSubstrings) {
760 const auto* str = to_remove[i];
761 const auto len = to_remove_len[i];
762
763 auto* pos = nameTmp;
764 while ((pos = strstr(pos, str)) != nullptr) {
765 const bool isBoundary = pos == nameTmp || pos[-1] == '<' || pos[-1] == ',' || pos[-1] == ' ';
766 if (!isBoundary) {
767 ++pos;
768 continue;
769 }
770
771 const auto tailMaxLen = (size_t)(MaxNameLength - (uint32_t)(pos + len - nameTmp));
772 memmove(pos, pos + len, GAIA_STRLEN(pos + len, tailMaxLen) + 1);
773 nameTmpLen -= len;
774 }
775 }
776
777 return nameTmpLen;
778 }
779
783 GAIA_NODISCARD bool copy_runtime_field(const RuntimeFieldInit& desc) {
784 if (m_symbols == nullptr || desc.name.empty() || desc.name.size() >= MaxNameLength ||
785 desc.unit.size() >= RuntimeFieldDesc::MaxUnitLength ||
786 desc.description.size() >= RuntimeDescriptionMaxLength)
787 return false;
788
789 RuntimeFieldDesc field{};
790 field.name = m_symbols->intern(desc.name);
791 field.type = desc.type;
792 field.offset = desc.offset;
793 field.count = desc.count;
794 field.semantic = desc.semantic;
795#if GAIA_JSON_ENABLED
796 field.jsonEncoding = desc.jsonEncoding;
797#endif
798 field.flags = desc.flags;
799 field.unit = m_symbols->intern(desc.unit);
800 field.minimum = desc.minimum;
801 field.maximum = desc.maximum;
802 field.step = desc.step;
803 field.description = m_symbols->intern(desc.description);
804 m_fields.push_back(field);
805 return true;
806 }
807
811 GAIA_NODISCARD bool copy_runtime_constant(const RuntimeConstantInit& desc) {
812 if (m_symbols == nullptr || desc.name.empty() || desc.name.size() >= MaxNameLength ||
813 desc.description.size() >= RuntimeDescriptionMaxLength)
814 return false;
815
816 RuntimeConstantDesc constant{};
817 constant.name = m_symbols->intern(desc.name);
818 constant.value = desc.value;
819 constant.description = m_symbols->intern(desc.description);
820 m_constants.push_back(constant);
821 return true;
822 }
823
824 template <typename T>
825 GAIA_NODISCARD static ComponentCacheItem*
826 create_typed(Entity entity, SymbolTable& symbols, const RuntimeTypeDesc* pRuntimeType) {
827 static_assert(core::is_raw_v<T>);
828
829 constexpr auto componentSize = detail::ComponentDesc<T>::size();
830 static_assert(
831 componentSize < Component::MaxComponentSizeInBytes,
832 "Trying to register a component larger than the maximum allowed component size! In the future this "
833 "restriction won't apply to components not stored inside archetype chunks.");
834
835 char nameTmp[MaxNameLength];
836 const auto nameTmpLen = init_type_name<T>(nameTmp);
837
838 uint8_t soaSizes[meta::StructToTupleMaxTypes]{};
839 auto desc = detail::ComponentDesc<T>::make(
840 util::str_view(nameTmp, nameTmpLen), std::span<uint8_t, meta::StructToTupleMaxTypes>{soaSizes});
841 if (pRuntimeType != nullptr) {
842 desc.runtimeType = *pRuntimeType;
843 }
844#if GAIA_ECS_AUTO_COMPONENT_FIELDS
845 else {
846 RuntimeFieldInit fields[meta::StructToTupleMaxTypes]{};
847 desc.runtimeType.fields = fields;
848 desc.runtimeType.fieldCount =
849 detail::ComponentDesc<T>::auto_fields(std::span<RuntimeFieldInit, meta::StructToTupleMaxTypes>{fields});
850 }
851#endif
852 return create(entity, symbols, desc);
853 }
854
855 public:
860 template <typename T>
861 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity) {
862 auto* symbols = new SymbolTable();
863 auto* item = create<T>(entity, *symbols);
864 item->m_ownedSymbols = symbols;
865 return item;
866 }
867
868 private:
873 template <typename T>
874 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, SymbolTable& symbols) {
875 return create_typed<T>(entity, symbols, nullptr);
876 }
877
878 public:
884 template <typename T>
885 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, const RuntimeTypeDesc& runtimeType) {
886 auto* symbols = new SymbolTable();
887 auto* item = create<T>(entity, *symbols, runtimeType);
888 item->m_ownedSymbols = symbols;
889 return item;
890 }
891
892 private:
898 template <typename T>
899 GAIA_NODISCARD static ComponentCacheItem*
900 create(Entity entity, SymbolTable& symbols, const RuntimeTypeDesc& runtimeType) {
901 return create_typed<T>(entity, symbols, &runtimeType);
902 }
903
908 GAIA_NODISCARD static ComponentCacheItem*
909 create(Entity entity, SymbolTable& symbols, const ecs::ComponentDesc& desc) {
910 GAIA_ASSERT(!desc.name.empty());
911 GAIA_ASSERT(desc.name.size() < MaxNameLength);
912 GAIA_ASSERT(desc.size < Component::MaxComponentSizeInBytes);
913 GAIA_ASSERT((desc.size == 0 && desc.alig == 0) || (desc.alig > 0 && desc.alig < Component::MaxAlignment));
914 GAIA_ASSERT(desc.alig == 0 || (desc.alig & (desc.alig - 1)) == 0);
915 GAIA_ASSERT(desc.soa <= meta::StructToTupleMaxTypes);
916 GAIA_ASSERT(desc.soa == 0 || desc.pSoaSizes != nullptr);
917#if GAIA_ASSERT_ENABLED
918 if (desc.soa > 0) {
919 uint32_t soaSize = 0;
920 GAIA_FOR(desc.soa) {
921 GAIA_ASSERT(desc.pSoaSizes[i] != 0);
922 soaSize += desc.pSoaSizes[i];
923 }
924 GAIA_ASSERT(soaSize <= desc.size);
925 }
926#endif
927
928 auto* cci = new ComponentCacheItem();
929 cci->m_symbols = &symbols;
930 cci->entity = entity;
931 cci->comp = Component(entity.id(), desc.soa, desc.size, desc.alig, desc.storageType);
932 cci->hashLookup = desc.hashLookup.hash != 0
933 ? desc.hashLookup
934 : ComponentLookupHash{core::calculate_hash64(desc.name.data(), desc.name.size())};
935
936 if (desc.soa > 0) {
937 GAIA_FOR(desc.soa) cci->soaSizes[i] = desc.pSoaSizes[i];
938 }
939
940 cci->name = symbols.intern(desc.name);
941 GAIA_ASSERT(cci->name.valid());
942
943 cci->func_ctor = desc.funcCtor;
944 cci->func_move_ctor = desc.funcMoveCtor;
945 cci->func_copy_ctor = desc.funcCopyCtor;
946 cci->func_dtor = desc.funcDtor;
947 cci->func_copy = desc.funcCopy;
948 cci->func_move = desc.funcMove;
949 cci->func_swap = desc.funcSwap;
950 cci->func_cmp = desc.funcCmp;
951 cci->func_save = desc.funcSave;
952 cci->func_load = desc.funcLoad;
953
954 const auto& runtimeType = desc.runtimeType;
955 GAIA_ASSERT(runtimeType.description.size() < RuntimeDescriptionMaxLength);
956 if (runtimeType.description.size() < RuntimeDescriptionMaxLength)
957 cci->description = symbols.intern(runtimeType.description);
958 cci->typeKind = runtimeType.typeKind;
959 cci->semantic = runtimeType.semantic;
960#if GAIA_JSON_ENABLED
961 cci->jsonEncoding = runtimeType.jsonEncoding;
962#endif
963 cci->underlyingType = runtimeType.underlyingType;
964 cci->elementType = runtimeType.elementType;
965 cci->elementCount = runtimeType.elementCount;
966 cci->opaqueAsType = runtimeType.opaqueAsType;
967 cci->sequenceAdapter = runtimeType.sequenceAdapter;
968 cci->opaqueAdapter = runtimeType.opaqueAdapter;
969
970 if (runtimeType.fieldCount > 0) {
971 GAIA_FOR(runtimeType.fieldCount) {
972 [[maybe_unused]] const bool copied = cci->copy_runtime_field(runtimeType.fields[i]);
973 GAIA_ASSERT(copied);
974 }
975 }
976
977 if (runtimeType.constantCount > 0) {
978 GAIA_FOR(runtimeType.constantCount) {
979 [[maybe_unused]] const bool copied = cci->copy_runtime_constant(runtimeType.constants[i]);
980 GAIA_ASSERT(copied);
981 }
982 }
983
984 return cci;
985 }
986
987 public:
992 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, const ecs::ComponentDesc& desc) {
993 auto* symbols = new SymbolTable();
994 auto* item = create(entity, *symbols, desc);
995 item->m_ownedSymbols = symbols;
996 return item;
997 }
998
1001 static void destroy(ComponentCacheItem* pItem) {
1002 if (pItem == nullptr)
1003 return;
1004
1005 auto* ownedSymbols = pItem->m_ownedSymbols;
1006 delete pItem;
1007 delete ownedSymbols;
1008 }
1009 };
1010 } // namespace ecs
1011} // 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