2#include "gaia/config/config.h"
8#include "gaia/cnt/darray.h"
9#include "gaia/cnt/map.h"
10#include "gaia/cnt/sparse_storage.h"
11#include "gaia/core/utility.h"
12#include "gaia/ecs/component_cache_item.h"
13#include "gaia/ecs/id.h"
14#include "gaia/mem/mem_alloc.h"
15#include "gaia/mem/smallblock_allocator.h"
22 struct SparseComponentRecord {
30 struct RuntimeSparseComponentRecord {
34 void* pData =
nullptr;
40 struct to_sparse_id<ecs::SparseComponentRecord<T>> {
41 static sparse_id
get(
const ecs::SparseComponentRecord<T>& item)
noexcept {
42 return (sparse_id)item.entity.id();
47 struct to_sparse_id<ecs::RuntimeSparseComponentRecord> {
48 static sparse_id
get(
const ecs::RuntimeSparseComponentRecord& item)
noexcept {
49 return (sparse_id)item.entity.id();
57 struct SparseComponentStoreErased {
59 void* pStore =
nullptr;
61 void* (*func_add)(
void*, Entity) =
nullptr;
63 void* (*func_mut)(
void*, Entity) =
nullptr;
65 const void* (*func_get)(
const void*, Entity) =
nullptr;
67 const void* (*func_get_entity)(
const void*, Entity) =
nullptr;
68 void (*func_del)(
void*, Entity) =
nullptr;
69 bool (*func_has)(
const void*, Entity) =
nullptr;
70 bool (*func_copy_entity)(
void*, Entity, Entity) =
nullptr;
71 uint32_t (*func_count)(
const void*) =
nullptr;
72 void (*func_collect_entities)(
const void*, cnt::darray<Entity>&) =
nullptr;
73 bool (*func_each_entity)(
const void*,
void*, bool (*)(
void*, Entity)) =
nullptr;
74 void (*func_clear_store)(
void*) =
nullptr;
75 void (*func_del_store)(
void*) =
nullptr;
79 struct RuntimeSparseComponentStore final {
80 GAIA_USE_SMALLBLOCK(RuntimeSparseComponentStore)
82 static constexpr uint32_t PayloadPageBytes = 16U * 1024U;
84 static constexpr uint32_t MaxPayloadsPerPage = 256U;
87 cnt::sparse_storage<RuntimeSparseComponentRecord> data;
89 cnt::map<EntityLookupKey, RuntimeSparseComponentRecord> pairData;
91 cnt::darray<void*> payloadPages;
93 const ComponentCacheItem* pItem =
nullptr;
95 void* pFreePayload =
nullptr;
97 uint32_t payloadStride = 0;
99 uint32_t payloadAlignment = 0;
101 uint32_t payloadsPerPage = 0;
103 uint32_t nextPayload = 0;
105 explicit RuntimeSparseComponentStore(
const ComponentCacheItem& item): pItem(&item) {
106 const auto size = item.comp.size();
110 const auto itemAlignment = item.comp.alig();
111 payloadAlignment = itemAlignment <
alignof(
void*) ? (uint32_t)
alignof(
void*) : itemAlignment;
112 const auto payloadSize = size <
sizeof(
void*) ? (uint32_t)
sizeof(
void*) : size;
113 payloadStride = mem::align(payloadSize, payloadAlignment);
114 payloadsPerPage = PayloadPageBytes / payloadStride;
115 if (payloadsPerPage == 0)
117 else if (payloadsPerPage > MaxPayloadsPerPage)
118 payloadsPerPage = MaxPayloadsPerPage;
119 nextPayload = payloadsPerPage;
122 static cnt::sparse_id sid(Entity entity) {
123 GAIA_ASSERT(!entity.pair());
124 return (cnt::sparse_id)entity.id();
129 GAIA_NODISCARD
void* create_payload() {
130 const auto size = pItem->comp.size();
134 auto* pData = alloc_payload();
135 GAIA_ASSERT(pData !=
nullptr);
136 if (pItem->func_ctor !=
nullptr)
137 pItem->func_ctor(pData, 1);
143 void destroy_payload(
void* pData) {
144 if (pData ==
nullptr)
152 GAIA_NODISCARD
void* alloc_payload() {
153 if (pFreePayload !=
nullptr) {
154 auto* pData = pFreePayload;
155 memcpy(&pFreePayload, pData,
sizeof(pFreePayload));
159 if (nextPayload == payloadsPerPage) {
160 const auto pageBytes = payloadStride * payloadsPerPage;
161 auto* pPage = payloadAlignment <=
alignof(std::max_align_t)
162 ? mem::mem_alloc(
"Runtime sparse payload page", pageBytes)
163 : mem::mem_alloc_alig(
"Runtime sparse payload page", pageBytes, payloadAlignment);
164 GAIA_ASSERT(pPage !=
nullptr);
165 payloadPages.push_back(pPage);
169 auto* pData = (uint8_t*)payloadPages.back() + ((uintptr_t)payloadStride * nextPayload);
175 void free_payload(
void* pData) {
176 memcpy(pData, &pFreePayload,
sizeof(pFreePayload));
177 pFreePayload = pData;
181 void free_payload_pages() {
182 for (
auto* pPage: payloadPages) {
183 if (payloadAlignment <=
alignof(std::max_align_t))
184 mem::mem_free(
"Runtime sparse payload page", pPage);
186 mem::mem_free_alig(
"Runtime sparse payload page", pPage);
188 payloadPages.clear();
189 pFreePayload =
nullptr;
190 nextPayload = payloadsPerPage;
193 void* add(Entity entity) {
194 if GAIA_UNLIKELY (entity.pair()) {
195 const auto key = EntityLookupKey(entity);
196 const auto it = pairData.find(key);
197 if (it != pairData.end())
198 return it->second.pData;
200 auto* pData = create_payload();
201 return pairData.emplace(key, RuntimeSparseComponentRecord{entity, pData}).first->second.pData;
204 const auto sparseId = sid(entity);
205 if (data.has(sparseId))
206 return data[sparseId].pData;
208 auto* pData = create_payload();
209 data.add(RuntimeSparseComponentRecord{entity, pData});
213 void* mut(Entity entity) {
214 if GAIA_UNLIKELY (entity.pair()) {
215 const auto it = pairData.find(EntityLookupKey(entity));
216 GAIA_ASSERT(it != pairData.end());
217 return it != pairData.end() ? it->second.pData :
nullptr;
220 GAIA_ASSERT(data.has(sid(entity)));
221 return data[sid(entity)].pData;
224 const void* get(Entity entity)
const {
225 if GAIA_UNLIKELY (entity.pair()) {
226 const auto it = pairData.find(EntityLookupKey(entity));
227 GAIA_ASSERT(it != pairData.end());
228 return it != pairData.end() ? it->second.pData :
nullptr;
231 GAIA_ASSERT(data.has(sid(entity)));
232 return data[sid(entity)].pData;
238 GAIA_NODISCARD
const void* get_entity(Entity entity)
const {
239 GAIA_ASSERT(!entity.pair());
240 GAIA_ASSERT(data.has(sid(entity)));
241 return data[sid(entity)].pData;
244 void del_entity(Entity entity) {
245 if GAIA_UNLIKELY (entity.pair()) {
246 const auto it = pairData.find(EntityLookupKey(entity));
247 if (it == pairData.end())
250 destroy_payload(it->second.pData);
255 const auto sparseId = sid(entity);
256 if (!data.has(sparseId))
259 destroy_payload(data[sparseId].pData);
263 bool has(Entity entity)
const {
264 if GAIA_UNLIKELY (entity.pair())
265 return pairData.contains(EntityLookupKey(entity));
267 return data.has(sid(entity));
270 bool copy_entity(Entity dstEntity, Entity srcEntity) {
274 auto* pDst = add(dstEntity);
275 const auto* pSrc = get(srcEntity);
277 pItem->copy(pDst, pSrc, 0, 0, pItem->comp.size(), pItem->comp.size());
281 uint32_t count()
const {
282 return (uint32_t)(data.size() + pairData.size());
285 void collect_entities(cnt::darray<Entity>& out)
const {
286 out.reserve(out.size() + count());
287 for (
const auto& item: data)
288 out.push_back(item.entity);
290 const auto firstPair = out.size();
291 for (
const auto& item: pairData)
292 out.push_back(item.first.entity());
293 core::sort(out.begin() + firstPair, out.end(), core::is_smaller<Entity>{});
300 GAIA_NODISCARD
bool each_entity(
void* ctx,
bool (*func)(
void*, Entity))
const {
301 for (
const auto& item: data) {
302 if (!func(ctx, item.entity))
306 if (pairData.empty())
309 cnt::darray<Entity> pairEntities;
310 pairEntities.reserve((uint32_t)pairData.size());
311 for (
const auto& item: pairData)
312 pairEntities.push_back(item.first.entity());
313 core::sort(pairEntities.begin(), pairEntities.end(), core::is_smaller<Entity>{});
314 for (
auto entity: pairEntities) {
315 if (!func(ctx, entity))
322 while (!data.empty())
323 del_entity(data.begin()->entity);
324 while (!pairData.empty())
325 del_entity(pairData.begin()->first.entity());
326 free_payload_pages();
331 template <
typename T>
332 struct SparseComponentStore final {
333 GAIA_USE_SMALLBLOCK(SparseComponentStore)
335 cnt::sparse_storage<SparseComponentRecord<T>> data;
337 cnt::map<EntityLookupKey, SparseComponentRecord<T>> pairData;
339 static cnt::sparse_id sid(Entity entity) {
340 GAIA_ASSERT(!entity.pair());
341 return (cnt::sparse_id)entity.id();
344 T& add(Entity entity) {
345 if GAIA_UNLIKELY (entity.pair()) {
346 const auto key = EntityLookupKey(entity);
347 const auto it = pairData.find(key);
348 if (it != pairData.end())
349 return it->second.value;
351 return pairData.emplace(key, SparseComponentRecord<T>{entity}).first->second.value;
354 const auto sparseId = sid(entity);
355 if (data.has(sparseId))
356 return data[sparseId].value;
358 auto& item = data.add(SparseComponentRecord<T>{entity});
362 T& mut(Entity entity) {
363 if GAIA_UNLIKELY (entity.pair()) {
364 const auto it = pairData.find(EntityLookupKey(entity));
365 GAIA_ASSERT(it != pairData.end());
366 return it->second.value;
369 GAIA_ASSERT(data.has(sid(entity)));
370 return data[sid(entity)].value;
373 const T& get(Entity entity)
const {
374 if GAIA_UNLIKELY (entity.pair()) {
375 return get_pair(entity);
378 return get_entity(entity);
384 GAIA_NODISCARD
const T& get_entity(Entity entity)
const {
385 GAIA_ASSERT(!entity.pair());
386 GAIA_ASSERT(data.has(sid(entity)));
387 return data[sid(entity)].value;
393 GAIA_NODISCARD
const T& get_pair(Entity entity)
const {
394 GAIA_ASSERT(entity.pair());
395 const auto it = pairData.find(EntityLookupKey(entity));
396 GAIA_ASSERT(it != pairData.end());
397 return it->second.value;
403 GAIA_NODISCARD T* try_mut(Entity entity) {
404 if GAIA_UNLIKELY (entity.pair()) {
405 const auto it = pairData.find(EntityLookupKey(entity));
406 return it != pairData.end() ? &it->second.value :
nullptr;
409 const auto sparseId = sid(entity);
410 return data.has(sparseId) ? &data[sparseId].value :
nullptr;
416 GAIA_NODISCARD
const T* try_get(Entity entity)
const {
417 if GAIA_UNLIKELY (entity.pair()) {
418 const auto it = pairData.find(EntityLookupKey(entity));
419 return it != pairData.end() ? &it->second.value :
nullptr;
422 const auto sparseId = sid(entity);
423 return data.has(sparseId) ? &data[sparseId].value :
nullptr;
426 void del_entity(Entity entity) {
427 if GAIA_UNLIKELY (entity.pair()) {
428 pairData.erase(EntityLookupKey(entity));
432 const auto sparseId = sid(entity);
433 if (data.has(sparseId))
437 bool has(Entity entity)
const {
438 if GAIA_UNLIKELY (entity.pair())
439 return pairData.contains(EntityLookupKey(entity));
441 return data.has(sid(entity));
444 bool copy_entity(Entity dstEntity, Entity srcEntity) {
447 add(dstEntity) = get(srcEntity);
451 uint32_t count()
const {
452 return (uint32_t)(data.size() + pairData.size());
455 void collect_entities(cnt::darray<Entity>& out)
const {
456 out.reserve(out.size() + count());
457 for (
const auto& item: data)
458 out.push_back(item.entity);
460 const auto firstPair = out.size();
461 for (
const auto& item: pairData)
462 out.push_back(item.first.entity());
463 core::sort(out.begin() + firstPair, out.end(), core::is_smaller<Entity>{});
470 GAIA_NODISCARD
bool each_entity(
void* ctx,
bool (*func)(
void*, Entity))
const {
471 for (
const auto& item: data) {
472 if (!func(ctx, item.entity))
476 if (pairData.empty())
479 cnt::darray<Entity> pairEntities;
480 pairEntities.reserve((uint32_t)pairData.size());
481 for (
const auto& item: pairData)
482 pairEntities.push_back(item.first.entity());
483 core::sort(pairEntities.begin(), pairEntities.end(), core::is_smaller<Entity>{});
484 for (
auto entity: pairEntities) {
485 if (!func(ctx, entity))
497 template <
typename Store>
498 static SparseComponentStoreErased make_sparse_component_store_erased(Store* pStore) {
499 SparseComponentStoreErased store{};
500 store.pStore = pStore;
501 store.func_add = [](
void* pStoreRaw, Entity entity) {
502 if constexpr (std::is_pointer_v<decltype(static_cast<Store*>(pStoreRaw)->add(entity))>)
503 return static_cast<Store*
>(pStoreRaw)->add(entity);
505 return (
void*)&
static_cast<Store*
>(pStoreRaw)->add(entity);
507 store.func_mut = [](
void* pStoreRaw, Entity entity) {
508 if constexpr (std::is_pointer_v<decltype(static_cast<Store*>(pStoreRaw)->mut(entity))>)
509 return static_cast<Store*
>(pStoreRaw)->mut(entity);
511 return (
void*)&
static_cast<Store*
>(pStoreRaw)->mut(entity);
513 store.func_get = [](
const void* pStoreRaw, Entity entity) {
514 if constexpr (std::is_pointer_v<decltype(static_cast<const Store*>(pStoreRaw)->get(entity))>)
515 return static_cast<const Store*
>(pStoreRaw)->get(entity);
517 return (
const void*)&
static_cast<const Store*
>(pStoreRaw)->get(entity);
519 store.func_get_entity = [](
const void* pStoreRaw, Entity entity) {
520 if constexpr (std::is_pointer_v<decltype(static_cast<const Store*>(pStoreRaw)->get_entity(entity))>)
521 return static_cast<const Store*
>(pStoreRaw)->get_entity(entity);
523 return (
const void*)&
static_cast<const Store*
>(pStoreRaw)->get_entity(entity);
525 store.func_del = [](
void* pStoreRaw, Entity entity) {
526 static_cast<Store*
>(pStoreRaw)->del_entity(entity);
528 store.func_has = [](
const void* pStoreRaw, Entity entity) {
529 return static_cast<const Store*
>(pStoreRaw)->has(entity);
531 store.func_copy_entity = [](
void* pStoreRaw, Entity dstEntity, Entity srcEntity) {
532 return static_cast<Store*
>(pStoreRaw)->copy_entity(dstEntity, srcEntity);
534 store.func_count = [](
const void* pStoreRaw) {
535 return static_cast<const Store*
>(pStoreRaw)->count();
537 store.func_collect_entities = [](
const void* pStoreRaw, cnt::darray<Entity>& out) {
538 static_cast<const Store*
>(pStoreRaw)->collect_entities(out);
540 store.func_each_entity = [](
const void* pStoreRaw,
void* pCtx, bool (*func)(
void*, Entity)) {
541 return static_cast<const Store*
>(pStoreRaw)->each_entity(pCtx, func);
543 store.func_clear_store = [](
void* pStoreRaw) {
544 static_cast<Store*
>(pStoreRaw)->clear_store();
546 store.func_del_store = [](
void* pStoreRaw) {
547 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