SDEngine
Game Engine
Loading...
Searching...
No Matches
SparseEntitySet.hpp
Go to the documentation of this file.
1// TODO(docs): Add file-level Doxygen header
2// - @file SparseEntitySet.hpp
3// - @brief Sparse set data structure for component storage
4// - Memory layout: sparse array -> dense array -> component data
5// - Performance characteristics (O(1) add/remove/get)
6#pragma once
7#include "Entity.hpp"
11
12namespace sd {
13// TODO(docs): Document SparseEntitySetBase interface
14// - Purpose: Type-erased base for component pools
15// - Used by EntityManager for heterogeneous storage
17public:
18 ~SparseEntitySetBase() override = default;
19 virtual bool remove(Entity entity) = 0;
20
21 virtual std::optional<ComponentDebugInfo> get_debug_info(Entity e) = 0;
22};
23
24// TODO(docs): Document SparseEntitySet class thoroughly
25// - Purpose: Sparse set storing components with O(1) operations
26// - Memory layout explanation (sparse pages, dense arrays)
27// - Page size rationale (1024 entities per page)
28// - Add: O(1) amortized (with page allocation)
29// - Remove: O(1) with swap-and-pop
30// - Get: O(1)
31// - Serialization/Deserialization for state persistence
32// - Example: How EntityManager uses this for component pools
38// TODO(vatnar): SparseEntity Set does make_unique on per page and push_back on each
39// component add. which trigger a heap allocation. Rather add an arena for this, one arena
40// per archetype or similar
41template<typename T>
43 static constexpr usize PAGE_SIZE = 1024;
44 static constexpr usize SHIFT = math::log2_int(PAGE_SIZE);
45 static constexpr usize MASK = PAGE_SIZE - 1;
46
47 // INVARIANTS:
48 // 1. dense_entities.size() == dense_data.size() (parallel arrays)
49 // 2. sparse[page][offset] is either valid dense index OR sentinel (max usize)
50 // 3. For each i in [0, dense_entities.size()): sparse[dense_entities[i]] points back to i
51
52#ifndef NDEBUG
53 void ValidateInvariants() const {
54 assert(dense_entities.size() == dense_data.size());
55 for (size_t i = 0; i < dense_entities.size(); ++i) {
57 usize page = e.index >> SHIFT;
58 usize offset = e.index & MASK;
59 assert(page < sparse.size() && sparse[page]);
61 }
62 }
63#endif
64
65 std::vector<std::unique_ptr<usize[]>> sparse;
66 std::vector<T> dense_data;
67 std::vector<Entity> dense_entities;
68
69public:
70 template<typename... Args>
71 void add(Entity entity, Args&&... args);
72
78 bool remove(Entity entity) override;
79
85 T* get(Entity entity);
86 [[nodiscard]] const T* get(Entity entity) const;
87
88 std::optional<ComponentDebugInfo> get_debug_info(Entity e) override;
89 T* operator[](const Entity idx) { return get(idx); }
90 [[nodiscard]] const std::vector<Entity>& get_dense_entities() const { return dense_entities; }
91
92 [[nodiscard]] usize size() const { return dense_entities.size(); }
93
94 void serialize_to(std::vector<char>& out) const {
95 // Header: size_t entity_count, size_t sizeof(T).
96 size_t entity_count = dense_entities.size();
97 size_t comp_size = sizeof(T);
98
99 out.resize(sizeof(size_t) * 2 + entity_count * (sizeof(Entity) + comp_size));
100 char* ptr = out.data();
101 memcpy(ptr, &entity_count, sizeof(size_t));
102 ptr += sizeof(size_t);
103 memcpy(ptr, &comp_size, sizeof(size_t));
104 ptr += sizeof(size_t);
105
106 // Dense entities + data.
107 memcpy(ptr, dense_entities.data(), entity_count * sizeof(Entity));
108 ptr += entity_count * sizeof(Entity);
110 }
111
112 void deserialize_from(const std::vector<char>& data) {
113 const char* ptr = data.data();
114 size_t entity_count, comp_size;
115 memcpy(&entity_count, ptr, sizeof(size_t));
116 ptr += sizeof(size_t);
117 memcpy(&comp_size, ptr, sizeof(size_t));
118 ptr += sizeof(size_t);
119
121 dense_data.resize(entity_count);
122
123 memcpy(dense_entities.data(), ptr, entity_count * sizeof(Entity));
124 ptr += entity_count * sizeof(Entity);
126
127 sparse.clear();
128 for (size_t i = 0; i < entity_count; ++i) {
130 const usize page = e.index >> SHIFT;
131 const usize offset = e.index & MASK;
132
133 if (page >= sparse.size())
134 sparse.resize(page + 1);
135 if (!sparse[page]) {
136 sparse[page] = std::make_unique<usize[]>(PAGE_SIZE); // TODO: here
137 std::fill_n(sparse[page].get(), PAGE_SIZE, std::numeric_limits<usize>::max());
138 }
139 sparse[page][offset] = i;
140 }
141#ifndef NDEBUG
143#endif
144 }
145
146 void serialize(Serializer& s) const override {
147 s.write(static_cast<u32>(dense_entities.size()));
148 for (const auto& e : dense_entities) {
149 s.write(e.index);
150 s.write(e.generation);
151 }
152 // Only serialize component data if it's serializable
153 if constexpr (SerializableComponent<T>) {
154 for (const auto& data : dense_data) {
156 }
157 }
158 }
159
160 void deserialize(Serializer& s) override {
161 u32 count = s.read<u32>();
162 dense_entities.resize(count);
163 for (u32 i = 0; i < count; ++i) {
164 dense_entities[i].index = s.read<u32>();
165 dense_entities[i].generation = s.read<u32>();
166 }
167 dense_data.resize(dense_entities.size());
168 if constexpr (SerializableComponent<T>) {
169 for (auto& data : dense_data) {
171 }
172 }
173 // Rebuild sparse index
174 sparse.clear();
175 for (size_t i = 0; i < dense_entities.size(); ++i) {
177 const usize page = e.index >> SHIFT;
178 const usize offset = e.index & MASK;
179
180 if (page >= sparse.size())
181 sparse.resize(page + 1);
182 if (!sparse[page]) {
183 // TODO: here
184 sparse[page] = std::make_unique<usize[]>(PAGE_SIZE);
185 std::fill_n(sparse[page].get(), PAGE_SIZE, std::numeric_limits<usize>::max());
186 }
187 sparse[page][offset] = i;
188 }
189 }
190
192};
193
195} // namespace sd
Definition RuntimeStateManager.hpp:24
Definition serialization.hpp:16
Definition serialization.hpp:36
Definition SparseEntitySet.hpp:16
~SparseEntitySetBase() override=default
virtual bool remove(Entity entity)=0
virtual std::optional< ComponentDebugInfo > get_debug_info(Entity e)=0
Definition SparseEntitySet.hpp:42
const std::vector< Entity > & get_dense_entities() const
Definition SparseEntitySet.hpp:90
static constexpr usize MASK
Definition SparseEntitySet.hpp:45
bool remove(Entity entity) override
Definition SparseEntitySet.inl:35
usize size() const
Definition SparseEntitySet.hpp:92
const T * get(Entity entity) const
void deserialize_from(const std::vector< char > &data)
Definition SparseEntitySet.hpp:112
static constexpr usize PAGE_SIZE
Definition SparseEntitySet.hpp:43
T * operator[](const Entity idx)
Definition SparseEntitySet.hpp:89
static constexpr usize SHIFT
Definition SparseEntitySet.hpp:44
T * get(Entity entity)
Definition SparseEntitySet.inl:74
void add(Entity entity, Args &&... args)
void deserialize(Serializer &s) override
Definition SparseEntitySet.hpp:160
void serialize(Serializer &s) const override
Definition SparseEntitySet.hpp:146
std::optional< ComponentDebugInfo > get_debug_info(Entity e) override
Definition SparseEntitySet.inl:113
std::vector< Entity > dense_entities
Definition SparseEntitySet.hpp:67
void ValidateInvariants() const
Definition SparseEntitySet.hpp:53
std::vector< std::unique_ptr< usize[]> > sparse
Definition SparseEntitySet.hpp:65
std::vector< T > dense_data
Definition SparseEntitySet.hpp:66
void serialize_to(std::vector< char > &out) const
Definition SparseEntitySet.hpp:94
Definition component_registration.hpp:107
consteval usize log2_int(std::unsigned_integral auto n)
Definition math_utils.hpp:10
Definition Application.hpp:28
static void deserialize(T &component, Serializer &s)=delete
static void serialize(const T &component, Serializer &s)=delete
Definition Entity.hpp:18
u32 index
Definition Entity.hpp:19
std::uint32_t u32
Definition types.hpp:15
constexpr T g_type_max
Definition types.hpp:21
std::size_t usize
Definition types.hpp:18