2#include "gaia/config/config.h"
8#include "gaia/cnt/darray.h"
9#include "gaia/cnt/sparse_storage.h"
10#include "gaia/ecs/component_cache_item.h"
11#include "gaia/ecs/id.h"
12#include "gaia/mem/mem_alloc.h"
13#include "gaia/mem/smallblock_allocator.h"
20 struct SparseComponentRecord {
28 struct RuntimeSparseComponentRecord {
32 void* pData =
nullptr;
38 struct to_sparse_id<ecs::SparseComponentRecord<T>> {
39 static sparse_id
get(
const ecs::SparseComponentRecord<T>& item)
noexcept {
40 return (sparse_id)item.entity.id();
45 struct to_sparse_id<ecs::RuntimeSparseComponentRecord> {
46 static sparse_id
get(
const ecs::RuntimeSparseComponentRecord& item)
noexcept {
47 return (sparse_id)item.entity.id();
55 struct SparseComponentStoreErased {
57 void* pStore =
nullptr;
59 void* (*func_add)(
void*, Entity) =
nullptr;
61 void* (*func_mut)(
void*, Entity) =
nullptr;
63 const void* (*func_get)(
const void*, Entity) =
nullptr;
64 void (*func_del)(
void*, Entity) =
nullptr;
65 bool (*func_has)(
const void*, Entity) =
nullptr;
66 bool (*func_copy_entity)(
void*, Entity, Entity) =
nullptr;
67 uint32_t (*func_count)(
const void*) =
nullptr;
68 void (*func_collect_entities)(
const void*, cnt::darray<Entity>&) =
nullptr;
69 bool (*func_for_each_entity)(
const void*,
void*, bool (*)(
void*, Entity)) =
nullptr;
70 void (*func_clear_store)(
void*) =
nullptr;
71 void (*func_del_store)(
void*) =
nullptr;
75 struct RuntimeSparseComponentStore final {
76 GAIA_USE_SMALLBLOCK(RuntimeSparseComponentStore)
78 static constexpr uint32_t PayloadPageBytes = 16U * 1024U;
80 static constexpr uint32_t MaxPayloadsPerPage = 256U;
83 cnt::sparse_storage<RuntimeSparseComponentRecord> data;
85 cnt::darray<void*> payloadPages;
87 const ComponentCacheItem* pItem =
nullptr;
89 void* pFreePayload =
nullptr;
91 uint32_t payloadStride = 0;
93 uint32_t payloadAlignment = 0;
95 uint32_t payloadsPerPage = 0;
97 uint32_t nextPayload = 0;
99 explicit RuntimeSparseComponentStore(
const ComponentCacheItem& item): pItem(&item) {
100 const auto size = item.comp.size();
104 const auto itemAlignment = item.comp.alig();
105 payloadAlignment = itemAlignment <
alignof(
void*) ? (uint32_t)
alignof(
void*) : itemAlignment;
106 const auto payloadSize = size <
sizeof(
void*) ? (uint32_t)
sizeof(
void*) : size;
107 payloadStride = mem::align(payloadSize, payloadAlignment);
108 payloadsPerPage = PayloadPageBytes / payloadStride;
109 if (payloadsPerPage == 0)
111 else if (payloadsPerPage > MaxPayloadsPerPage)
112 payloadsPerPage = MaxPayloadsPerPage;
113 nextPayload = payloadsPerPage;
116 static cnt::sparse_id sid(Entity entity) {
117 return (cnt::sparse_id)entity.id();
121 GAIA_NODISCARD
void* alloc_payload() {
122 if (pFreePayload !=
nullptr) {
123 auto* pData = pFreePayload;
124 memcpy(&pFreePayload, pData,
sizeof(pFreePayload));
128 if (nextPayload == payloadsPerPage) {
129 const auto pageBytes = payloadStride * payloadsPerPage;
130 auto* pPage = payloadAlignment <=
alignof(std::max_align_t)
131 ? mem::mem_alloc(
"Runtime sparse payload page", pageBytes)
132 : mem::mem_alloc_alig(
"Runtime sparse payload page", pageBytes, payloadAlignment);
133 GAIA_ASSERT(pPage !=
nullptr);
134 payloadPages.push_back(pPage);
138 auto* pData = (uint8_t*)payloadPages.back() + ((uintptr_t)payloadStride * nextPayload);
144 void free_payload(
void* pData) {
145 memcpy(pData, &pFreePayload,
sizeof(pFreePayload));
146 pFreePayload = pData;
150 void free_payload_pages() {
151 for (
auto* pPage: payloadPages) {
152 if (payloadAlignment <=
alignof(std::max_align_t))
153 mem::mem_free(
"Runtime sparse payload page", pPage);
155 mem::mem_free_alig(
"Runtime sparse payload page", pPage);
157 payloadPages.clear();
158 pFreePayload =
nullptr;
159 nextPayload = payloadsPerPage;
162 void* add(Entity entity) {
163 const auto sparseId = sid(entity);
164 if (data.has(sparseId))
165 return data[sparseId].pData;
167 void* pData =
nullptr;
168 const auto size = pItem->comp.size();
170 pData = alloc_payload();
171 GAIA_ASSERT(pData !=
nullptr);
172 if (pItem->func_ctor !=
nullptr)
173 pItem->func_ctor(pData, 1);
176 data.add(RuntimeSparseComponentRecord{entity, pData});
180 void* mut(Entity entity) {
181 GAIA_ASSERT(data.has(sid(entity)));
182 return data[sid(entity)].pData;
185 const void* get(Entity entity)
const {
186 GAIA_ASSERT(data.has(sid(entity)));
187 return data[sid(entity)].pData;
190 void del_entity(Entity entity) {
191 const auto sparseId = sid(entity);
192 if (!data.has(sparseId))
195 auto* pData = data[sparseId].pData;
196 if (pData !=
nullptr) {
203 bool has(Entity entity)
const {
204 return data.has(sid(entity));
207 bool copy_entity(Entity dstEntity, Entity srcEntity) {
211 auto* pDst = add(dstEntity);
212 const auto* pSrc = get(srcEntity);
214 pItem->copy(pDst, pSrc, 0, 0, pItem->comp.size(), pItem->comp.size());
218 uint32_t count()
const {
219 return (uint32_t)data.size();
222 void collect_entities(cnt::darray<Entity>& out)
const {
223 out.reserve(out.size() + (uint32_t)data.size());
224 for (
const auto& item: data)
225 out.push_back(item.entity);
229 while (!data.empty())
230 del_entity(data.begin()->entity);
231 free_payload_pages();
236 template <
typename T>
237 struct SparseComponentStore final {
238 GAIA_USE_SMALLBLOCK(SparseComponentStore)
240 cnt::sparse_storage<SparseComponentRecord<T>> data;
242 static cnt::sparse_id sid(Entity entity) {
243 return (cnt::sparse_id)entity.id();
246 T& add(Entity entity) {
247 const auto sparseId = sid(entity);
248 if (data.has(sparseId))
249 return data[sparseId].value;
251 auto& item = data.add(SparseComponentRecord<T>{entity});
255 T& mut(Entity entity) {
256 GAIA_ASSERT(data.has(sid(entity)));
257 return data[sid(entity)].value;
260 const T& get(Entity entity)
const {
261 GAIA_ASSERT(data.has(sid(entity)));
262 return data[sid(entity)].value;
265 void del_entity(Entity entity) {
266 const auto sparseId = sid(entity);
267 if (data.has(sparseId))
271 bool has(Entity entity)
const {
272 return data.has(sid(entity));
275 bool copy_entity(Entity dstEntity, Entity srcEntity) {
278 add(dstEntity) = get(srcEntity);
282 uint32_t count()
const {
283 return (uint32_t)data.size();
286 void collect_entities(cnt::darray<Entity>& out)
const {
287 out.reserve(out.size() + (uint32_t)data.size());
288 for (
const auto& item: data)
289 out.push_back(item.entity);
297 template <
typename Store>
298 static SparseComponentStoreErased make_sparse_component_store_erased(Store* pStore) {
299 SparseComponentStoreErased store{};
300 store.pStore = pStore;
301 store.func_add = [](
void* pStoreRaw, Entity entity) {
302 if constexpr (std::is_pointer_v<decltype(static_cast<Store*>(pStoreRaw)->add(entity))>)
303 return static_cast<Store*
>(pStoreRaw)->add(entity);
305 return (
void*)&
static_cast<Store*
>(pStoreRaw)->add(entity);
307 store.func_mut = [](
void* pStoreRaw, Entity entity) {
308 if constexpr (std::is_pointer_v<decltype(static_cast<Store*>(pStoreRaw)->mut(entity))>)
309 return static_cast<Store*
>(pStoreRaw)->mut(entity);
311 return (
void*)&
static_cast<Store*
>(pStoreRaw)->mut(entity);
313 store.func_get = [](
const void* pStoreRaw, Entity entity) {
314 if constexpr (std::is_pointer_v<decltype(static_cast<const Store*>(pStoreRaw)->get(entity))>)
315 return static_cast<const Store*
>(pStoreRaw)->get(entity);
317 return (
const void*)&
static_cast<const Store*
>(pStoreRaw)->get(entity);
319 store.func_del = [](
void* pStoreRaw, Entity entity) {
320 static_cast<Store*
>(pStoreRaw)->del_entity(entity);
322 store.func_has = [](
const void* pStoreRaw, Entity entity) {
323 return static_cast<const Store*
>(pStoreRaw)->has(entity);
325 store.func_copy_entity = [](
void* pStoreRaw, Entity dstEntity, Entity srcEntity) {
326 return static_cast<Store*
>(pStoreRaw)->copy_entity(dstEntity, srcEntity);
328 store.func_count = [](
const void* pStoreRaw) {
329 return static_cast<const Store*
>(pStoreRaw)->count();
331 store.func_collect_entities = [](
const void* pStoreRaw, cnt::darray<Entity>& out) {
332 static_cast<const Store*
>(pStoreRaw)->collect_entities(out);
334 store.func_for_each_entity = [](
const void* pStoreRaw,
void* pCtx, bool (*func)(
void*, Entity)) {
335 const auto& data =
static_cast<const Store*
>(pStoreRaw)->data;
336 for (
const auto& item: data) {
337 if (!func(pCtx, item.entity))
342 store.func_clear_store = [](
void* pStoreRaw) {
343 static_cast<Store*
>(pStoreRaw)->clear_store();
345 store.func_del_store = [](
void* pStoreRaw) {
346 delete static_cast<Store*
>(pStoreRaw);
static sparse_id get(const T &item) noexcept
Returns the sparse identifier for an item.
Definition sparse_storage.h:53