Properly link flecs library
This commit is contained in:
388
engine/libs/flecs/src/storage/entity_index.c
Normal file
388
engine/libs/flecs/src/storage/entity_index.c
Normal file
@@ -0,0 +1,388 @@
|
||||
#include "../private_api.h"
|
||||
|
||||
static
|
||||
ecs_entity_index_page_t* flecs_entity_index_ensure_page(
|
||||
ecs_entity_index_t *index,
|
||||
uint32_t id)
|
||||
{
|
||||
int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS);
|
||||
if (page_index >= ecs_vec_count(&index->pages)) {
|
||||
ecs_vec_set_min_count_zeromem_t(index->allocator, &index->pages,
|
||||
ecs_entity_index_page_t*, page_index + 1);
|
||||
}
|
||||
|
||||
ecs_entity_index_page_t **page_ptr = ecs_vec_get_t(&index->pages,
|
||||
ecs_entity_index_page_t*, page_index);
|
||||
ecs_entity_index_page_t *page = *page_ptr;
|
||||
if (!page) {
|
||||
page = *page_ptr = flecs_bcalloc(&index->page_allocator);
|
||||
ecs_assert(page != NULL, ECS_OUT_OF_MEMORY, NULL);
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void flecs_entity_index_init(
|
||||
ecs_allocator_t *allocator,
|
||||
ecs_entity_index_t *index)
|
||||
{
|
||||
index->allocator = allocator;
|
||||
index->alive_count = 1;
|
||||
ecs_vec_init_t(allocator, &index->dense, uint64_t, 1);
|
||||
ecs_vec_set_count_t(allocator, &index->dense, uint64_t, 1);
|
||||
ecs_vec_init_t(allocator, &index->pages, ecs_entity_index_page_t*, 0);
|
||||
flecs_ballocator_init(&index->page_allocator,
|
||||
ECS_SIZEOF(ecs_entity_index_page_t));
|
||||
}
|
||||
|
||||
void flecs_entity_index_fini(
|
||||
ecs_entity_index_t *index)
|
||||
{
|
||||
ecs_vec_fini_t(index->allocator, &index->dense, uint64_t);
|
||||
#if defined(FLECS_SANITIZE) || defined(FLECS_USE_OS_ALLOC)
|
||||
int32_t i, count = ecs_vec_count(&index->pages);
|
||||
ecs_entity_index_page_t **pages = ecs_vec_first(&index->pages);
|
||||
for (i = 0; i < count; i ++) {
|
||||
flecs_bfree(&index->page_allocator, pages[i]);
|
||||
}
|
||||
#endif
|
||||
ecs_vec_fini_t(index->allocator, &index->pages, ecs_entity_index_page_t*);
|
||||
flecs_ballocator_fini(&index->page_allocator);
|
||||
}
|
||||
|
||||
ecs_record_t* flecs_entity_index_get_any(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
uint32_t id = (uint32_t)entity;
|
||||
int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS);
|
||||
ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages,
|
||||
ecs_entity_index_page_t*, page_index)[0];
|
||||
ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK];
|
||||
ecs_assert(r->dense != 0, ECS_INVALID_PARAMETER, NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
ecs_record_t* flecs_entity_index_get(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
ecs_record_t *r = flecs_entity_index_get_any(index, entity);
|
||||
ecs_assert(r->dense < index->alive_count, ECS_INVALID_PARAMETER, NULL);
|
||||
ecs_assert(ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] == entity,
|
||||
ECS_INVALID_PARAMETER, NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
ecs_record_t* flecs_entity_index_try_get_any(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
uint32_t id = (uint32_t)entity;
|
||||
int32_t page_index = (int32_t)(id >> FLECS_ENTITY_PAGE_BITS);
|
||||
if (page_index >= ecs_vec_count(&index->pages)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ecs_entity_index_page_t *page = ecs_vec_get_t(&index->pages,
|
||||
ecs_entity_index_page_t*, page_index)[0];
|
||||
if (!page) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK];
|
||||
if (!r->dense) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ecs_record_t* flecs_entity_index_try_get(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
ecs_record_t *r = flecs_entity_index_try_get_any(index, entity);
|
||||
if (r) {
|
||||
if (r->dense >= index->alive_count) {
|
||||
return NULL;
|
||||
}
|
||||
if (ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] != entity) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ecs_record_t* flecs_entity_index_ensure(
|
||||
ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
uint32_t id = (uint32_t)entity;
|
||||
ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id);
|
||||
ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK];
|
||||
|
||||
int32_t dense = r->dense;
|
||||
if (dense) {
|
||||
/* Entity is already alive, nothing to be done */
|
||||
if (dense < index->alive_count) {
|
||||
ecs_assert(
|
||||
ecs_vec_get_t(&index->dense, uint64_t, dense)[0] == entity,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
/* Entity doesn't have a dense index yet */
|
||||
ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = entity;
|
||||
r->dense = dense = ecs_vec_count(&index->dense) - 1;
|
||||
index->max_id = id > index->max_id ? id : index->max_id;
|
||||
}
|
||||
|
||||
ecs_assert(dense != 0, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
/* Entity is not alive, swap with first not alive element */
|
||||
uint64_t *ids = ecs_vec_first(&index->dense);
|
||||
uint64_t e_swap = ids[index->alive_count];
|
||||
ecs_record_t *r_swap = flecs_entity_index_get_any(index, e_swap);
|
||||
ecs_assert(r_swap->dense == index->alive_count,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
r_swap->dense = dense;
|
||||
r->dense = index->alive_count;
|
||||
ids[dense] = e_swap;
|
||||
ids[index->alive_count ++] = entity;
|
||||
|
||||
ecs_assert(flecs_entity_index_is_alive(index, entity),
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void flecs_entity_index_remove(
|
||||
ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
ecs_record_t *r = flecs_entity_index_try_get(index, entity);
|
||||
if (!r) {
|
||||
/* Entity is not alive or doesn't exist, nothing to be done */
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t dense = r->dense;
|
||||
int32_t i_swap = -- index->alive_count;
|
||||
uint64_t *e_swap_ptr = ecs_vec_get_t(&index->dense, uint64_t, i_swap);
|
||||
uint64_t e_swap = e_swap_ptr[0];
|
||||
ecs_record_t *r_swap = flecs_entity_index_get_any(index, e_swap);
|
||||
ecs_assert(r_swap->dense == i_swap, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
r_swap->dense = dense;
|
||||
r->table = NULL;
|
||||
r->idr = NULL;
|
||||
r->row = 0;
|
||||
r->dense = i_swap;
|
||||
ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = e_swap;
|
||||
e_swap_ptr[0] = ECS_GENERATION_INC(entity);
|
||||
ecs_assert(!flecs_entity_index_is_alive(index, entity),
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
}
|
||||
|
||||
void flecs_entity_index_set_generation(
|
||||
ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
ecs_record_t *r = flecs_entity_index_try_get_any(index, entity);
|
||||
if (r) {
|
||||
ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0] = entity;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t flecs_entity_index_get_generation(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
ecs_record_t *r = flecs_entity_index_try_get_any(index, entity);
|
||||
if (r) {
|
||||
return ecs_vec_get_t(&index->dense, uint64_t, r->dense)[0];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool flecs_entity_index_is_alive(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
return flecs_entity_index_try_get(index, entity) != NULL;
|
||||
}
|
||||
|
||||
bool flecs_entity_index_is_valid(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
uint32_t id = (uint32_t)entity;
|
||||
ecs_record_t *r = flecs_entity_index_try_get_any(index, id);
|
||||
if (!r || !r->dense) {
|
||||
/* Doesn't exist yet, so is valid */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the id exists, it must be alive */
|
||||
return r->dense < index->alive_count;
|
||||
}
|
||||
|
||||
bool flecs_entity_index_exists(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity)
|
||||
{
|
||||
return flecs_entity_index_try_get_any(index, entity) != NULL;
|
||||
}
|
||||
|
||||
uint64_t flecs_entity_index_new_id(
|
||||
ecs_entity_index_t *index)
|
||||
{
|
||||
if (index->alive_count != ecs_vec_count(&index->dense)) {
|
||||
/* Recycle id */
|
||||
return ecs_vec_get_t(&index->dense, uint64_t, index->alive_count ++)[0];
|
||||
}
|
||||
|
||||
/* Create new id */
|
||||
uint32_t id = (uint32_t)++ index->max_id;
|
||||
ecs_vec_append_t(index->allocator, &index->dense, uint64_t)[0] = id;
|
||||
|
||||
ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id);
|
||||
ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK];
|
||||
r->dense = index->alive_count ++;
|
||||
ecs_assert(index->alive_count == ecs_vec_count(&index->dense),
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
uint64_t* flecs_entity_index_new_ids(
|
||||
ecs_entity_index_t *index,
|
||||
int32_t count)
|
||||
{
|
||||
int32_t alive_count = index->alive_count;
|
||||
int32_t new_count = alive_count + count;
|
||||
int32_t dense_count = ecs_vec_count(&index->dense);
|
||||
|
||||
if (new_count < dense_count) {
|
||||
/* Recycle ids */
|
||||
index->alive_count = new_count;
|
||||
return ecs_vec_get_t(&index->dense, uint64_t, alive_count);
|
||||
}
|
||||
|
||||
/* Allocate new ids */
|
||||
ecs_vec_set_count_t(index->allocator, &index->dense, uint64_t, new_count);
|
||||
int32_t i, to_add = new_count - dense_count;
|
||||
for (i = 0; i < to_add; i ++) {
|
||||
uint32_t id = (uint32_t)++ index->max_id;
|
||||
int32_t dense = dense_count + i;
|
||||
ecs_vec_get_t(&index->dense, uint64_t, dense)[0] = id;
|
||||
ecs_entity_index_page_t *page = flecs_entity_index_ensure_page(index, id);
|
||||
ecs_assert(page != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_record_t *r = &page->records[id & FLECS_ENTITY_PAGE_MASK];
|
||||
r->dense = dense;
|
||||
}
|
||||
|
||||
index->alive_count = new_count;
|
||||
return ecs_vec_get_t(&index->dense, uint64_t, alive_count);
|
||||
}
|
||||
|
||||
void flecs_entity_index_set_size(
|
||||
ecs_entity_index_t *index,
|
||||
int32_t size)
|
||||
{
|
||||
ecs_vec_set_size_t(index->allocator, &index->dense, uint64_t, size);
|
||||
}
|
||||
|
||||
int32_t flecs_entity_index_count(
|
||||
const ecs_entity_index_t *index)
|
||||
{
|
||||
return index->alive_count - 1;
|
||||
}
|
||||
|
||||
int32_t flecs_entity_index_size(
|
||||
const ecs_entity_index_t *index)
|
||||
{
|
||||
return ecs_vec_count(&index->dense) - 1;
|
||||
}
|
||||
|
||||
int32_t flecs_entity_index_not_alive_count(
|
||||
const ecs_entity_index_t *index)
|
||||
{
|
||||
return ecs_vec_count(&index->dense) - index->alive_count;
|
||||
}
|
||||
|
||||
void flecs_entity_index_clear(
|
||||
ecs_entity_index_t *index)
|
||||
{
|
||||
int32_t i, count = ecs_vec_count(&index->pages);
|
||||
ecs_entity_index_page_t **pages = ecs_vec_first_t(&index->pages,
|
||||
ecs_entity_index_page_t*);
|
||||
for (i = 0; i < count; i ++) {
|
||||
ecs_entity_index_page_t *page = pages[i];
|
||||
if (page) {
|
||||
ecs_os_zeromem(page);
|
||||
}
|
||||
}
|
||||
|
||||
ecs_vec_set_count_t(index->allocator, &index->dense, uint64_t, 1);
|
||||
|
||||
index->alive_count = 1;
|
||||
index->max_id = 0;
|
||||
}
|
||||
|
||||
const uint64_t* flecs_entity_index_ids(
|
||||
const ecs_entity_index_t *index)
|
||||
{
|
||||
return ecs_vec_get_t(&index->dense, uint64_t, 1);
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_entity_index_copy_intern(
|
||||
ecs_entity_index_t * dst,
|
||||
const ecs_entity_index_t * src)
|
||||
{
|
||||
flecs_entity_index_set_size(dst, flecs_entity_index_size(src));
|
||||
const uint64_t *ids = flecs_entity_index_ids(src);
|
||||
|
||||
int32_t i, count = src->alive_count;
|
||||
for (i = 0; i < count - 1; i ++) {
|
||||
uint64_t id = ids[i];
|
||||
ecs_record_t *src_ptr = flecs_entity_index_get(src, id);
|
||||
ecs_record_t *dst_ptr = flecs_entity_index_ensure(dst, id);
|
||||
flecs_entity_index_set_generation(dst, id);
|
||||
ecs_os_memcpy_t(dst_ptr, src_ptr, ecs_record_t);
|
||||
}
|
||||
|
||||
dst->max_id = src->max_id;
|
||||
|
||||
ecs_assert(src->alive_count == dst->alive_count, ECS_INTERNAL_ERROR, NULL);
|
||||
}
|
||||
|
||||
void flecs_entity_index_copy(
|
||||
ecs_entity_index_t *dst,
|
||||
const ecs_entity_index_t *src)
|
||||
{
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
|
||||
flecs_entity_index_init(src->allocator, dst);
|
||||
flecs_entity_index_copy_intern(dst, src);
|
||||
}
|
||||
|
||||
void flecs_entity_index_restore(
|
||||
ecs_entity_index_t *dst,
|
||||
const ecs_entity_index_t *src)
|
||||
{
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
|
||||
flecs_entity_index_clear(dst);
|
||||
flecs_entity_index_copy_intern(dst, src);
|
||||
}
|
||||
158
engine/libs/flecs/src/storage/entity_index.h
Normal file
158
engine/libs/flecs/src/storage/entity_index.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @file datastructures/entity_index.h
|
||||
* @brief Entity index data structure.
|
||||
*
|
||||
* The entity index stores the table, row for an entity id.
|
||||
*/
|
||||
|
||||
#ifndef FLECS_ENTITY_INDEX_H
|
||||
#define FLECS_ENTITY_INDEX_H
|
||||
|
||||
#define FLECS_ENTITY_PAGE_SIZE (1 << FLECS_ENTITY_PAGE_BITS)
|
||||
#define FLECS_ENTITY_PAGE_MASK (FLECS_ENTITY_PAGE_SIZE - 1)
|
||||
|
||||
typedef struct ecs_entity_index_page_t {
|
||||
ecs_record_t records[FLECS_ENTITY_PAGE_SIZE];
|
||||
} ecs_entity_index_page_t;
|
||||
|
||||
typedef struct ecs_entity_index_t {
|
||||
ecs_vec_t dense;
|
||||
ecs_vec_t pages;
|
||||
int32_t alive_count;
|
||||
uint64_t max_id;
|
||||
ecs_block_allocator_t page_allocator;
|
||||
ecs_allocator_t *allocator;
|
||||
} ecs_entity_index_t;
|
||||
|
||||
/** Initialize entity index. */
|
||||
void flecs_entity_index_init(
|
||||
ecs_allocator_t *allocator,
|
||||
ecs_entity_index_t *index);
|
||||
|
||||
/** Deinitialize entity index. */
|
||||
void flecs_entity_index_fini(
|
||||
ecs_entity_index_t *index);
|
||||
|
||||
/* Get entity (must exist/must be alive) */
|
||||
ecs_record_t* flecs_entity_index_get(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Get entity (must exist/may not be alive) */
|
||||
ecs_record_t* flecs_entity_index_get_any(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Get entity (may not exist/must be alive) */
|
||||
ecs_record_t* flecs_entity_index_try_get(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Get entity (may not exist/may not be alive) */
|
||||
ecs_record_t* flecs_entity_index_try_get_any(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/** Ensure entity exists. */
|
||||
ecs_record_t* flecs_entity_index_ensure(
|
||||
ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Remove entity */
|
||||
void flecs_entity_index_remove(
|
||||
ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Set generation of entity */
|
||||
void flecs_entity_index_set_generation(
|
||||
ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Get current generation of entity */
|
||||
uint64_t flecs_entity_index_get_generation(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Return whether entity is alive */
|
||||
bool flecs_entity_index_is_alive(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Return whether entity is valid */
|
||||
bool flecs_entity_index_is_valid(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Return whether entity exists */
|
||||
bool flecs_entity_index_exists(
|
||||
const ecs_entity_index_t *index,
|
||||
uint64_t entity);
|
||||
|
||||
/* Create or recycle entity id */
|
||||
uint64_t flecs_entity_index_new_id(
|
||||
ecs_entity_index_t *index);
|
||||
|
||||
/* Bulk create or recycle new entity ids */
|
||||
uint64_t* flecs_entity_index_new_ids(
|
||||
ecs_entity_index_t *index,
|
||||
int32_t count);
|
||||
|
||||
/* Set size of index */
|
||||
void flecs_entity_index_set_size(
|
||||
ecs_entity_index_t *index,
|
||||
int32_t size);
|
||||
|
||||
/* Return number of entities in index */
|
||||
int32_t flecs_entity_index_count(
|
||||
const ecs_entity_index_t *index);
|
||||
|
||||
/* Return number of allocated entities in index */
|
||||
int32_t flecs_entity_index_size(
|
||||
const ecs_entity_index_t *index);
|
||||
|
||||
/* Return number of not alive entities in index */
|
||||
int32_t flecs_entity_index_not_alive_count(
|
||||
const ecs_entity_index_t *index);
|
||||
|
||||
/* Clear entity index */
|
||||
void flecs_entity_index_clear(
|
||||
ecs_entity_index_t *index);
|
||||
|
||||
/* Return number of alive entities in index */
|
||||
const uint64_t* flecs_entity_index_ids(
|
||||
const ecs_entity_index_t *index);
|
||||
|
||||
void flecs_entity_index_copy(
|
||||
ecs_entity_index_t *dst,
|
||||
const ecs_entity_index_t *src);
|
||||
|
||||
void flecs_entity_index_restore(
|
||||
ecs_entity_index_t *dst,
|
||||
const ecs_entity_index_t *src);
|
||||
|
||||
#define ecs_eis(world) (&((world)->store.entity_index))
|
||||
#define flecs_entities_init(world) flecs_entity_index_init(&world->allocator, ecs_eis(world))
|
||||
#define flecs_entities_fini(world) flecs_entity_index_fini(ecs_eis(world))
|
||||
#define flecs_entities_get(world, entity) flecs_entity_index_get(ecs_eis(world), entity)
|
||||
#define flecs_entities_try(world, entity) flecs_entity_index_try_get(ecs_eis(world), entity)
|
||||
#define flecs_entities_get_any(world, entity) flecs_entity_index_get_any(ecs_eis(world), entity)
|
||||
#define flecs_entities_ensure(world, entity) flecs_entity_index_ensure(ecs_eis(world), entity)
|
||||
#define flecs_entities_remove(world, entity) flecs_entity_index_remove(ecs_eis(world), entity)
|
||||
#define flecs_entities_set_generation(world, entity) flecs_entity_index_set_generation(ecs_eis(world), entity)
|
||||
#define flecs_entities_get_generation(world, entity) flecs_entity_index_get_generation(ecs_eis(world), entity)
|
||||
#define flecs_entities_is_alive(world, entity) flecs_entity_index_is_alive(ecs_eis(world), entity)
|
||||
#define flecs_entities_is_valid(world, entity) flecs_entity_index_is_valid(ecs_eis(world), entity)
|
||||
#define flecs_entities_exists(world, entity) flecs_entity_index_exists(ecs_eis(world), entity)
|
||||
#define flecs_entities_new_id(world) flecs_entity_index_new_id(ecs_eis(world))
|
||||
#define flecs_entities_new_ids(world, count) flecs_entity_index_new_ids(ecs_eis(world), count)
|
||||
#define flecs_entities_max_id(world) (ecs_eis(world)->max_id)
|
||||
#define flecs_entities_set_size(world, size) flecs_entity_index_set_size(ecs_eis(world), size)
|
||||
#define flecs_entities_count(world) flecs_entity_index_count(ecs_eis(world))
|
||||
#define flecs_entities_size(world) flecs_entity_index_size(ecs_eis(world))
|
||||
#define flecs_entities_not_alive_count(world) flecs_entity_index_not_alive_count(ecs_eis(world))
|
||||
#define flecs_entities_clear(world) flecs_entity_index_clear(ecs_eis(world))
|
||||
#define flecs_entities_ids(world) flecs_entity_index_ids(ecs_eis(world))
|
||||
#define flecs_entities_copy(dst, src) flecs_entity_index_copy(dst, src)
|
||||
#define flecs_entities_restore(dst, src) flecs_entity_index_restore(dst, src)
|
||||
|
||||
#endif
|
||||
614
engine/libs/flecs/src/storage/id_index.c
Normal file
614
engine/libs/flecs/src/storage/id_index.c
Normal file
@@ -0,0 +1,614 @@
|
||||
/**
|
||||
* @file id_index.c
|
||||
* @brief Index for looking up tables by (component) id.
|
||||
*
|
||||
* An id record stores the administration for an in use (component) id, that is
|
||||
* an id that has been used in tables.
|
||||
*
|
||||
* An id record contains a table cache, which stores the list of tables that
|
||||
* have the id. Each entry in the cache (a table record) stores the first
|
||||
* occurrence of the id in the table and the number of occurrences of the id in
|
||||
* the table (in the case of wildcard ids).
|
||||
*
|
||||
* Id records are used in lots of scenarios, like uncached queries, or for
|
||||
* getting a component array/component for an entity.
|
||||
*/
|
||||
|
||||
#include "../private_api.h"
|
||||
|
||||
static
|
||||
ecs_id_record_elem_t* flecs_id_record_elem(
|
||||
ecs_id_record_t *head,
|
||||
ecs_id_record_elem_t *list,
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
return ECS_OFFSET(idr, (uintptr_t)list - (uintptr_t)head);
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_id_record_elem_insert(
|
||||
ecs_id_record_t *head,
|
||||
ecs_id_record_t *idr,
|
||||
ecs_id_record_elem_t *elem)
|
||||
{
|
||||
ecs_id_record_elem_t *head_elem = flecs_id_record_elem(idr, elem, head);
|
||||
ecs_id_record_t *cur = head_elem->next;
|
||||
elem->next = cur;
|
||||
elem->prev = head;
|
||||
if (cur) {
|
||||
ecs_id_record_elem_t *cur_elem = flecs_id_record_elem(idr, elem, cur);
|
||||
cur_elem->prev = idr;
|
||||
}
|
||||
head_elem->next = idr;
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_id_record_elem_remove(
|
||||
ecs_id_record_t *idr,
|
||||
ecs_id_record_elem_t *elem)
|
||||
{
|
||||
ecs_id_record_t *prev = elem->prev;
|
||||
ecs_id_record_t *next = elem->next;
|
||||
ecs_assert(prev != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_id_record_elem_t *prev_elem = flecs_id_record_elem(idr, elem, prev);
|
||||
prev_elem->next = next;
|
||||
if (next) {
|
||||
ecs_id_record_elem_t *next_elem = flecs_id_record_elem(idr, elem, next);
|
||||
next_elem->prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_insert_id_elem(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr,
|
||||
ecs_id_t wildcard,
|
||||
ecs_id_record_t *widr)
|
||||
{
|
||||
ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL);
|
||||
if (!widr) {
|
||||
widr = flecs_id_record_ensure(world, wildcard);
|
||||
}
|
||||
ecs_assert(widr != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) {
|
||||
ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
flecs_id_record_elem_insert(widr, idr, &idr->first);
|
||||
} else {
|
||||
ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
flecs_id_record_elem_insert(widr, idr, &idr->second);
|
||||
|
||||
if (idr->flags & EcsIdTraversable) {
|
||||
flecs_id_record_elem_insert(widr, idr, &idr->trav);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_remove_id_elem(
|
||||
ecs_id_record_t *idr,
|
||||
ecs_id_t wildcard)
|
||||
{
|
||||
ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) {
|
||||
ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
flecs_id_record_elem_remove(idr, &idr->first);
|
||||
} else {
|
||||
ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
flecs_id_record_elem_remove(idr, &idr->second);
|
||||
|
||||
if (idr->flags & EcsIdTraversable) {
|
||||
flecs_id_record_elem_remove(idr, &idr->trav);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
ecs_id_t flecs_id_record_hash(
|
||||
ecs_id_t id)
|
||||
{
|
||||
id = ecs_strip_generation(id);
|
||||
if (ECS_IS_PAIR(id)) {
|
||||
ecs_entity_t r = ECS_PAIR_FIRST(id);
|
||||
ecs_entity_t o = ECS_PAIR_SECOND(id);
|
||||
if (r == EcsAny) {
|
||||
r = EcsWildcard;
|
||||
}
|
||||
if (o == EcsAny) {
|
||||
o = EcsWildcard;
|
||||
}
|
||||
id = ecs_pair(r, o);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static
|
||||
ecs_id_record_t* flecs_id_record_new(
|
||||
ecs_world_t *world,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_id_record_t *idr, *idr_t = NULL;
|
||||
ecs_id_t hash = flecs_id_record_hash(id);
|
||||
if (hash >= FLECS_HI_ID_RECORD_ID) {
|
||||
idr = flecs_bcalloc(&world->allocators.id_record);
|
||||
ecs_map_insert_ptr(&world->id_index_hi, hash, idr);
|
||||
} else {
|
||||
idr = &world->id_index_lo[hash];
|
||||
ecs_os_zeromem(idr);
|
||||
}
|
||||
|
||||
ecs_table_cache_init(world, &idr->cache);
|
||||
|
||||
idr->id = id;
|
||||
idr->refcount = 1;
|
||||
idr->reachable.current = -1;
|
||||
|
||||
bool is_wildcard = ecs_id_is_wildcard(id);
|
||||
bool is_pair = ECS_IS_PAIR(id);
|
||||
|
||||
ecs_entity_t rel = 0, tgt = 0, role = id & ECS_ID_FLAGS_MASK;
|
||||
if (is_pair) {
|
||||
// rel = ecs_pair_first(world, id);
|
||||
rel = ECS_PAIR_FIRST(id);
|
||||
ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
/* Relationship object can be 0, as tables without a ChildOf
|
||||
* relationship are added to the (ChildOf, 0) id record */
|
||||
tgt = ECS_PAIR_SECOND(id);
|
||||
|
||||
#ifdef FLECS_DEBUG
|
||||
/* Check constraints */
|
||||
if (tgt) {
|
||||
tgt = ecs_get_alive(world, tgt);
|
||||
ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL);
|
||||
}
|
||||
if (tgt && !ecs_id_is_wildcard(tgt)) {
|
||||
/* Check if target of relationship satisfies OneOf property */
|
||||
ecs_entity_t oneof = flecs_get_oneof(world, rel);
|
||||
ecs_check( !oneof || ecs_has_pair(world, tgt, EcsChildOf, oneof),
|
||||
ECS_CONSTRAINT_VIOLATED, NULL);
|
||||
(void)oneof;
|
||||
|
||||
/* Check if we're not trying to inherit from a final target */
|
||||
if (rel == EcsIsA) {
|
||||
bool is_final = ecs_has_id(world, tgt, EcsFinal);
|
||||
ecs_check(!is_final, ECS_CONSTRAINT_VIOLATED,
|
||||
"cannot inherit from final entity");
|
||||
(void)is_final;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_wildcard && (rel != EcsFlag)) {
|
||||
/* Inherit flags from (relationship, *) record */
|
||||
ecs_id_record_t *idr_r = flecs_id_record_ensure(
|
||||
world, ecs_pair(rel, EcsWildcard));
|
||||
idr->parent = idr_r;
|
||||
idr->flags = idr_r->flags;
|
||||
|
||||
/* If pair is not a wildcard, append it to wildcard lists. These
|
||||
* allow for quickly enumerating all relationships for an object,
|
||||
* or all objecs for a relationship. */
|
||||
flecs_insert_id_elem(world, idr, ecs_pair(rel, EcsWildcard), idr_r);
|
||||
|
||||
idr_t = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, tgt));
|
||||
flecs_insert_id_elem(world, idr, ecs_pair(EcsWildcard, tgt), idr_t);
|
||||
|
||||
if (rel == EcsUnion) {
|
||||
idr->flags |= EcsIdUnion;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rel = id & ECS_COMPONENT_MASK;
|
||||
ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL);
|
||||
}
|
||||
|
||||
/* Initialize type info if id is not a tag */
|
||||
if (!is_wildcard && (!role || is_pair)) {
|
||||
if (!(idr->flags & EcsIdTag)) {
|
||||
const ecs_type_info_t *ti = flecs_type_info_get(world, rel);
|
||||
if (!ti && tgt) {
|
||||
ti = flecs_type_info_get(world, tgt);
|
||||
}
|
||||
idr->type_info = ti;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark entities that are used as component/pair ids. When a tracked
|
||||
* entity is deleted, cleanup policies are applied so that the store
|
||||
* won't contain any tables with deleted ids. */
|
||||
|
||||
/* Flag for OnDelete policies */
|
||||
flecs_add_flag(world, rel, EcsEntityIsId);
|
||||
if (tgt) {
|
||||
/* Flag for OnDeleteTarget policies */
|
||||
ecs_record_t *tgt_r = flecs_entities_get_any(world, tgt);
|
||||
ecs_assert(tgt_r != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
flecs_record_add_flag(tgt_r, EcsEntityIsTarget);
|
||||
if (idr->flags & EcsIdTraversable) {
|
||||
/* Flag used to determine if object should be traversed when
|
||||
* propagating events or with super/subset queries */
|
||||
flecs_record_add_flag(tgt_r, EcsEntityIsTraversable);
|
||||
|
||||
/* Add reference to (*, tgt) id record to entity record */
|
||||
tgt_r->idr = idr_t;
|
||||
}
|
||||
}
|
||||
|
||||
ecs_observable_t *o = &world->observable;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnAdd) * EcsIdHasOnAdd;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnRemove) * EcsIdHasOnRemove;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnSet) * EcsIdHasOnSet;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsUnSet) * EcsIdHasUnSet;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnTableFill) * EcsIdHasOnTableFill;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnTableEmpty) * EcsIdHasOnTableEmpty;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnTableCreate) * EcsIdHasOnTableCreate;
|
||||
idr->flags |= flecs_observers_exist(o, id, EcsOnTableDelete) * EcsIdHasOnTableDelete;
|
||||
|
||||
if (ecs_should_log_1()) {
|
||||
char *id_str = ecs_id_str(world, id);
|
||||
ecs_dbg_1("#[green]id#[normal] %s #[green]created", id_str);
|
||||
ecs_os_free(id_str);
|
||||
}
|
||||
|
||||
/* Update counters */
|
||||
world->info.id_create_total ++;
|
||||
|
||||
if (!is_wildcard) {
|
||||
world->info.id_count ++;
|
||||
|
||||
if (idr->type_info) {
|
||||
world->info.component_id_count ++;
|
||||
} else {
|
||||
world->info.tag_id_count ++;
|
||||
}
|
||||
|
||||
if (is_pair) {
|
||||
world->info.pair_id_count ++;
|
||||
}
|
||||
} else {
|
||||
world->info.wildcard_id_count ++;
|
||||
}
|
||||
|
||||
return idr;
|
||||
#ifdef FLECS_DEBUG
|
||||
error:
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_id_record_assert_empty(
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
(void)idr;
|
||||
ecs_assert(flecs_table_cache_count(&idr->cache) == 0,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(flecs_table_cache_empty_count(&idr->cache) == 0,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_id_record_free(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
ecs_poly_assert(world, ecs_world_t);
|
||||
ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_id_t id = idr->id;
|
||||
|
||||
flecs_id_record_assert_empty(idr);
|
||||
|
||||
/* Id is still in use by a filter, query, rule or observer */
|
||||
ecs_assert((world->flags & EcsWorldQuit) || (idr->keep_alive == 0),
|
||||
ECS_ID_IN_USE, "cannot delete id that is queried for");
|
||||
|
||||
if (ECS_IS_PAIR(id)) {
|
||||
ecs_entity_t rel = ECS_PAIR_FIRST(id);
|
||||
ecs_entity_t tgt = ECS_PAIR_SECOND(id);
|
||||
if (!ecs_id_is_wildcard(id)) {
|
||||
if (ECS_PAIR_FIRST(id) != EcsFlag) {
|
||||
/* If id is not a wildcard, remove it from the wildcard lists */
|
||||
flecs_remove_id_elem(idr, ecs_pair(rel, EcsWildcard));
|
||||
flecs_remove_id_elem(idr, ecs_pair(EcsWildcard, tgt));
|
||||
}
|
||||
} else {
|
||||
ecs_log_push_2();
|
||||
|
||||
/* If id is a wildcard, it means that all id records that match the
|
||||
* wildcard are also empty, so release them */
|
||||
if (ECS_PAIR_FIRST(id) == EcsWildcard) {
|
||||
/* Iterate (*, Target) list */
|
||||
ecs_id_record_t *cur, *next = idr->second.next;
|
||||
while ((cur = next)) {
|
||||
flecs_id_record_assert_empty(cur);
|
||||
next = cur->second.next;
|
||||
flecs_id_record_release(world, cur);
|
||||
}
|
||||
} else {
|
||||
/* Iterate (Relationship, *) list */
|
||||
ecs_assert(ECS_PAIR_SECOND(id) == EcsWildcard,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_id_record_t *cur, *next = idr->first.next;
|
||||
while ((cur = next)) {
|
||||
flecs_id_record_assert_empty(cur);
|
||||
next = cur->first.next;
|
||||
flecs_id_record_release(world, cur);
|
||||
}
|
||||
}
|
||||
|
||||
ecs_log_pop_2();
|
||||
}
|
||||
}
|
||||
|
||||
/* Update counters */
|
||||
world->info.id_delete_total ++;
|
||||
|
||||
if (!ecs_id_is_wildcard(id)) {
|
||||
world->info.id_count --;
|
||||
|
||||
if (ECS_IS_PAIR(id)) {
|
||||
world->info.pair_id_count --;
|
||||
}
|
||||
|
||||
if (idr->type_info) {
|
||||
world->info.component_id_count --;
|
||||
} else {
|
||||
world->info.tag_id_count --;
|
||||
}
|
||||
} else {
|
||||
world->info.wildcard_id_count --;
|
||||
}
|
||||
|
||||
/* Unregister the id record from the world & free resources */
|
||||
ecs_table_cache_fini(&idr->cache);
|
||||
flecs_name_index_free(idr->name_index);
|
||||
ecs_vec_fini_t(&world->allocator, &idr->reachable.ids, ecs_reachable_elem_t);
|
||||
|
||||
ecs_id_t hash = flecs_id_record_hash(id);
|
||||
if (hash >= FLECS_HI_ID_RECORD_ID) {
|
||||
ecs_map_remove(&world->id_index_hi, hash);
|
||||
flecs_bfree(&world->allocators.id_record, idr);
|
||||
} else {
|
||||
idr->id = 0; /* Tombstone */
|
||||
}
|
||||
|
||||
if (ecs_should_log_1()) {
|
||||
char *id_str = ecs_id_str(world, id);
|
||||
ecs_dbg_1("#[green]id#[normal] %s #[red]deleted", id_str);
|
||||
ecs_os_free(id_str);
|
||||
}
|
||||
}
|
||||
|
||||
ecs_id_record_t* flecs_id_record_ensure(
|
||||
ecs_world_t *world,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
||||
if (!idr) {
|
||||
idr = flecs_id_record_new(world, id);
|
||||
}
|
||||
return idr;
|
||||
}
|
||||
|
||||
ecs_id_record_t* flecs_id_record_get(
|
||||
const ecs_world_t *world,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_poly_assert(world, ecs_world_t);
|
||||
if (id == ecs_pair(EcsIsA, EcsWildcard)) {
|
||||
return world->idr_isa_wildcard;
|
||||
} else if (id == ecs_pair(EcsChildOf, EcsWildcard)) {
|
||||
return world->idr_childof_wildcard;
|
||||
} else if (id == ecs_pair_t(EcsIdentifier, EcsName)) {
|
||||
return world->idr_identifier_name;
|
||||
}
|
||||
|
||||
ecs_id_t hash = flecs_id_record_hash(id);
|
||||
ecs_id_record_t *idr = NULL;
|
||||
if (hash >= FLECS_HI_ID_RECORD_ID) {
|
||||
idr = ecs_map_get_deref(&world->id_index_hi, ecs_id_record_t, hash);
|
||||
} else {
|
||||
idr = &world->id_index_lo[hash];
|
||||
if (!idr->id) {
|
||||
idr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return idr;
|
||||
}
|
||||
|
||||
ecs_id_record_t* flecs_query_id_record_get(
|
||||
const ecs_world_t *world,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
||||
if (!idr) {
|
||||
ecs_entity_t first = ECS_PAIR_FIRST(id);
|
||||
if (ECS_IS_PAIR(id) && (first != EcsWildcard)) {
|
||||
idr = flecs_id_record_get(world, ecs_pair(EcsUnion, first));
|
||||
}
|
||||
return idr;
|
||||
}
|
||||
if (ECS_IS_PAIR(id) &&
|
||||
ECS_PAIR_SECOND(id) == EcsWildcard &&
|
||||
(idr->flags & EcsIdUnion))
|
||||
{
|
||||
idr = flecs_id_record_get(world,
|
||||
ecs_pair(EcsUnion, ECS_PAIR_FIRST(id)));
|
||||
}
|
||||
|
||||
return idr;
|
||||
}
|
||||
|
||||
void flecs_id_record_claim(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
(void)world;
|
||||
idr->refcount ++;
|
||||
}
|
||||
|
||||
int32_t flecs_id_record_release(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
int32_t rc = -- idr->refcount;
|
||||
ecs_assert(rc >= 0, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
if (!rc) {
|
||||
flecs_id_record_free(world, idr);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void flecs_id_record_release_tables(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
/* Cache should not contain tables that aren't empty */
|
||||
ecs_assert(flecs_table_cache_count(&idr->cache) == 0,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_table_cache_iter_t it;
|
||||
if (flecs_table_cache_empty_iter(&idr->cache, &it)) {
|
||||
ecs_table_record_t *tr;
|
||||
while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) {
|
||||
/* Release current table */
|
||||
flecs_table_free(world, tr->hdr.table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool flecs_id_record_set_type_info(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr,
|
||||
const ecs_type_info_t *ti)
|
||||
{
|
||||
bool is_wildcard = ecs_id_is_wildcard(idr->id);
|
||||
if (!is_wildcard) {
|
||||
if (ti) {
|
||||
if (!idr->type_info) {
|
||||
world->info.tag_id_count --;
|
||||
world->info.component_id_count ++;
|
||||
}
|
||||
} else {
|
||||
if (idr->type_info) {
|
||||
world->info.tag_id_count ++;
|
||||
world->info.component_id_count --;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = idr->type_info != ti;
|
||||
idr->type_info = ti;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
ecs_hashmap_t* flecs_id_record_name_index_ensure(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr)
|
||||
{
|
||||
ecs_hashmap_t *map = idr->name_index;
|
||||
if (!map) {
|
||||
map = idr->name_index = flecs_name_index_new(world, &world->allocator);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
ecs_hashmap_t* flecs_id_name_index_ensure(
|
||||
ecs_world_t *world,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_poly_assert(world, ecs_world_t);
|
||||
|
||||
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
||||
ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
return flecs_id_record_name_index_ensure(world, idr);
|
||||
}
|
||||
|
||||
ecs_hashmap_t* flecs_id_name_index_get(
|
||||
const ecs_world_t *world,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_poly_assert(world, ecs_world_t);
|
||||
|
||||
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
||||
if (!idr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return idr->name_index;
|
||||
}
|
||||
|
||||
ecs_table_record_t* flecs_table_record_get(
|
||||
const ecs_world_t *world,
|
||||
const ecs_table_t *table,
|
||||
ecs_id_t id)
|
||||
{
|
||||
ecs_poly_assert(world, ecs_world_t);
|
||||
|
||||
ecs_id_record_t* idr = flecs_id_record_get(world, id);
|
||||
if (!idr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table);
|
||||
}
|
||||
|
||||
ecs_table_record_t* flecs_id_record_get_table(
|
||||
const ecs_id_record_t *idr,
|
||||
const ecs_table_t *table)
|
||||
{
|
||||
ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table);
|
||||
}
|
||||
|
||||
void flecs_init_id_records(
|
||||
ecs_world_t *world)
|
||||
{
|
||||
/* Cache often used id records on world */
|
||||
world->idr_wildcard = flecs_id_record_ensure(world, EcsWildcard);
|
||||
world->idr_wildcard_wildcard = flecs_id_record_ensure(world,
|
||||
ecs_pair(EcsWildcard, EcsWildcard));
|
||||
world->idr_any = flecs_id_record_ensure(world, EcsAny);
|
||||
world->idr_isa_wildcard = flecs_id_record_ensure(world,
|
||||
ecs_pair(EcsIsA, EcsWildcard));
|
||||
}
|
||||
|
||||
void flecs_fini_id_records(
|
||||
ecs_world_t *world)
|
||||
{
|
||||
/* Loop & delete first element until there are no elements left. Id records
|
||||
* can recursively delete each other, this ensures we always have a
|
||||
* valid iterator. */
|
||||
while (ecs_map_count(&world->id_index_hi) > 0) {
|
||||
ecs_map_iter_t it = ecs_map_iter(&world->id_index_hi);
|
||||
ecs_map_next(&it);
|
||||
flecs_id_record_release(world, ecs_map_ptr(&it));
|
||||
}
|
||||
|
||||
int32_t i;
|
||||
for (i = 0; i < FLECS_HI_ID_RECORD_ID; i ++) {
|
||||
ecs_id_record_t *idr = &world->id_index_lo[i];
|
||||
if (idr->id) {
|
||||
flecs_id_record_release(world, idr);
|
||||
}
|
||||
}
|
||||
|
||||
ecs_assert(ecs_map_count(&world->id_index_hi) == 0,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_map_fini(&world->id_index_hi);
|
||||
ecs_os_free(world->id_index_lo);
|
||||
}
|
||||
147
engine/libs/flecs/src/storage/id_index.h
Normal file
147
engine/libs/flecs/src/storage/id_index.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @file id_index.h
|
||||
* @brief Index for looking up tables by (component) id.
|
||||
*/
|
||||
|
||||
#ifndef FLECS_ID_INDEX_H
|
||||
#define FLECS_ID_INDEX_H
|
||||
|
||||
/* Payload for id cache */
|
||||
struct ecs_table_record_t {
|
||||
ecs_table_cache_hdr_t hdr; /* Table cache header */
|
||||
int16_t index; /* First type index where id occurs in table */
|
||||
int16_t count; /* Number of times id occurs in table */
|
||||
int16_t column; /* First column index where id occurs */
|
||||
};
|
||||
|
||||
/* Linked list of id records */
|
||||
typedef struct ecs_id_record_elem_t {
|
||||
struct ecs_id_record_t *prev, *next;
|
||||
} ecs_id_record_elem_t;
|
||||
|
||||
typedef struct ecs_reachable_elem_t {
|
||||
const ecs_table_record_t *tr;
|
||||
ecs_record_t *record;
|
||||
ecs_entity_t src;
|
||||
ecs_id_t id;
|
||||
#ifndef NDEBUG
|
||||
ecs_table_t *table;
|
||||
#endif
|
||||
} ecs_reachable_elem_t;
|
||||
|
||||
typedef struct ecs_reachable_cache_t {
|
||||
int32_t generation;
|
||||
int32_t current;
|
||||
ecs_vec_t ids; /* vec<reachable_elem_t> */
|
||||
} ecs_reachable_cache_t;
|
||||
|
||||
/* Payload for id index which contains all datastructures for an id. */
|
||||
struct ecs_id_record_t {
|
||||
/* Cache with all tables that contain the id. Must be first member. */
|
||||
ecs_table_cache_t cache; /* table_cache<ecs_table_record_t> */
|
||||
|
||||
/* Id of record */
|
||||
ecs_id_t id;
|
||||
|
||||
/* Flags for id */
|
||||
ecs_flags32_t flags;
|
||||
|
||||
/* Cached pointer to type info for id, if id contains data. */
|
||||
const ecs_type_info_t *type_info;
|
||||
|
||||
/* Name lookup index (currently only used for ChildOf pairs) */
|
||||
ecs_hashmap_t *name_index;
|
||||
|
||||
/* Lists for all id records that match a pair wildcard. The wildcard id
|
||||
* record is at the head of the list. */
|
||||
ecs_id_record_elem_t first; /* (R, *) */
|
||||
ecs_id_record_elem_t second; /* (*, O) */
|
||||
ecs_id_record_elem_t trav; /* (*, O) with only traversable relationships */
|
||||
|
||||
/* Parent id record. For pair records the parent is the (R, *) record. */
|
||||
ecs_id_record_t *parent;
|
||||
|
||||
/* Refcount */
|
||||
int32_t refcount;
|
||||
|
||||
/* Keep alive count. This count must be 0 when the id record is deleted. If
|
||||
* it is not 0, an application attempted to delete an id that was still
|
||||
* queried for. */
|
||||
int32_t keep_alive;
|
||||
|
||||
/* Cache invalidation counter */
|
||||
ecs_reachable_cache_t reachable;
|
||||
};
|
||||
|
||||
/* Get id record for id */
|
||||
ecs_id_record_t* flecs_id_record_get(
|
||||
const ecs_world_t *world,
|
||||
ecs_id_t id);
|
||||
|
||||
/* Get id record for id for searching.
|
||||
* Same as flecs_id_record_get, but replaces (R, *) with (Union, R) if R is a
|
||||
* union relationship. */
|
||||
ecs_id_record_t* flecs_query_id_record_get(
|
||||
const ecs_world_t *world,
|
||||
ecs_id_t id);
|
||||
|
||||
/* Ensure id record for id */
|
||||
ecs_id_record_t* flecs_id_record_ensure(
|
||||
ecs_world_t *world,
|
||||
ecs_id_t id);
|
||||
|
||||
/* Increase refcount of id record */
|
||||
void flecs_id_record_claim(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr);
|
||||
|
||||
/* Decrease refcount of id record, delete if 0 */
|
||||
int32_t flecs_id_record_release(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr);
|
||||
|
||||
/* Release all empty tables in id record */
|
||||
void flecs_id_record_release_tables(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr);
|
||||
|
||||
/* Set (component) type info for id record */
|
||||
bool flecs_id_record_set_type_info(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr,
|
||||
const ecs_type_info_t *ti);
|
||||
|
||||
/* Ensure id record has name index */
|
||||
ecs_hashmap_t* flecs_id_name_index_ensure(
|
||||
ecs_world_t *world,
|
||||
ecs_id_t id);
|
||||
|
||||
ecs_hashmap_t* flecs_id_record_name_index_ensure(
|
||||
ecs_world_t *world,
|
||||
ecs_id_record_t *idr);
|
||||
|
||||
/* Get name index for id record */
|
||||
ecs_hashmap_t* flecs_id_name_index_get(
|
||||
const ecs_world_t *world,
|
||||
ecs_id_t id);
|
||||
|
||||
/* Find table record for id */
|
||||
ecs_table_record_t* flecs_table_record_get(
|
||||
const ecs_world_t *world,
|
||||
const ecs_table_t *table,
|
||||
ecs_id_t id);
|
||||
|
||||
/* Find table record for id record */
|
||||
ecs_table_record_t* flecs_id_record_get_table(
|
||||
const ecs_id_record_t *idr,
|
||||
const ecs_table_t *table);
|
||||
|
||||
/* Bootstrap cached id records */
|
||||
void flecs_init_id_records(
|
||||
ecs_world_t *world);
|
||||
|
||||
/* Cleanup all id records in world */
|
||||
void flecs_fini_id_records(
|
||||
ecs_world_t *world);
|
||||
|
||||
#endif
|
||||
2583
engine/libs/flecs/src/storage/table.c
Normal file
2583
engine/libs/flecs/src/storage/table.c
Normal file
File diff suppressed because it is too large
Load Diff
259
engine/libs/flecs/src/storage/table.h
Normal file
259
engine/libs/flecs/src/storage/table.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* @file table.h
|
||||
* @brief Table storage implementation.
|
||||
*/
|
||||
|
||||
#ifndef FLECS_TABLE_H
|
||||
#define FLECS_TABLE_H
|
||||
|
||||
#include "table_graph.h"
|
||||
|
||||
/* Table event type for notifying tables of world events */
|
||||
typedef enum ecs_table_eventkind_t {
|
||||
EcsTableTriggersForId,
|
||||
EcsTableNoTriggersForId,
|
||||
} ecs_table_eventkind_t;
|
||||
|
||||
typedef struct ecs_table_event_t {
|
||||
ecs_table_eventkind_t kind;
|
||||
|
||||
/* Query event */
|
||||
ecs_query_t *query;
|
||||
|
||||
/* Component info event */
|
||||
ecs_entity_t component;
|
||||
|
||||
/* Event match */
|
||||
ecs_entity_t event;
|
||||
|
||||
/* If the nubmer of fields gets out of hand, this can be turned into a union
|
||||
* but since events are very temporary objects, this works for now and makes
|
||||
* initializing an event a bit simpler. */
|
||||
} ecs_table_event_t;
|
||||
|
||||
/** Infrequently accessed data not stored inline in ecs_table_t */
|
||||
typedef struct ecs_table__t {
|
||||
uint64_t hash; /* Type hash */
|
||||
int32_t lock; /* Prevents modifications */
|
||||
int32_t traversable_count; /* Traversable relationship targets in table */
|
||||
uint16_t generation; /* Used for table cleanup */
|
||||
int16_t record_count; /* Table record count including wildcards */
|
||||
|
||||
struct ecs_table_record_t *records; /* Array with table records */
|
||||
ecs_hashmap_t *name_index; /* Cached pointer to name index */
|
||||
|
||||
ecs_switch_t *sw_columns; /* Switch columns */
|
||||
ecs_bitset_t *bs_columns; /* Bitset columns */
|
||||
int16_t sw_count;
|
||||
int16_t sw_offset;
|
||||
int16_t bs_count;
|
||||
int16_t bs_offset;
|
||||
int16_t ft_offset;
|
||||
} ecs_table__t;
|
||||
|
||||
/** Table column */
|
||||
typedef struct ecs_column_t {
|
||||
ecs_vec_t data; /* Vector with component data */
|
||||
ecs_id_t id; /* Component id */
|
||||
ecs_type_info_t *ti; /* Component type info */
|
||||
ecs_size_t size; /* Component size */
|
||||
} ecs_column_t;
|
||||
|
||||
/** Table data */
|
||||
struct ecs_data_t {
|
||||
ecs_vec_t entities; /* Entity ids */
|
||||
ecs_column_t *columns; /* Component data */
|
||||
};
|
||||
|
||||
/** A table is the Flecs equivalent of an archetype. Tables store all entities
|
||||
* with a specific set of components. Tables are automatically created when an
|
||||
* entity has a set of components not previously observed before. When a new
|
||||
* table is created, it is automatically matched with existing queries */
|
||||
struct ecs_table_t {
|
||||
uint64_t id; /* Table id in sparse set */
|
||||
ecs_flags32_t flags; /* Flags for testing table properties */
|
||||
int16_t column_count; /* Number of components (excluding tags) */
|
||||
ecs_type_t type; /* Vector with component ids */
|
||||
|
||||
ecs_data_t data; /* Component storage */
|
||||
ecs_graph_node_t node; /* Graph node */
|
||||
|
||||
int32_t *dirty_state; /* Keep track of changes in columns */
|
||||
int32_t *column_map; /* Map type index <-> column
|
||||
* - 0..count(T): type index -> column
|
||||
* - count(T)..count(C): column -> type index
|
||||
*/
|
||||
|
||||
ecs_table__t *_; /* Infrequently accessed table metadata */
|
||||
};
|
||||
|
||||
/* Init table */
|
||||
void flecs_table_init(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_table_t *from);
|
||||
|
||||
/** Copy type. */
|
||||
ecs_type_t flecs_type_copy(
|
||||
ecs_world_t *world,
|
||||
const ecs_type_t *src);
|
||||
|
||||
/** Free type. */
|
||||
void flecs_type_free(
|
||||
ecs_world_t *world,
|
||||
ecs_type_t *type);
|
||||
|
||||
/** Find or create table for a set of components */
|
||||
ecs_table_t* flecs_table_find_or_create(
|
||||
ecs_world_t *world,
|
||||
ecs_type_t *type);
|
||||
|
||||
/* Initialize columns for data */
|
||||
void flecs_table_init_data(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Clear all entities from a table. */
|
||||
void flecs_table_clear_entities(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Reset a table to its initial state */
|
||||
void flecs_table_reset(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Clear all entities from the table. Do not invoke OnRemove systems */
|
||||
void flecs_table_clear_entities_silent(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Clear table data. Don't call OnRemove handlers. */
|
||||
void flecs_table_clear_data(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_data_t *data);
|
||||
|
||||
/* Return number of entities in data */
|
||||
int32_t flecs_table_data_count(
|
||||
const ecs_data_t *data);
|
||||
|
||||
/* Add a new entry to the table for the specified entity */
|
||||
int32_t flecs_table_append(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_entity_t entity,
|
||||
bool construct,
|
||||
bool on_add);
|
||||
|
||||
/* Delete an entity from the table. */
|
||||
void flecs_table_delete(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
int32_t index,
|
||||
bool destruct);
|
||||
|
||||
/* Make sure table records are in correct table cache list */
|
||||
bool flecs_table_records_update_empty(
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Move a row from one table to another */
|
||||
void flecs_table_move(
|
||||
ecs_world_t *world,
|
||||
ecs_entity_t dst_entity,
|
||||
ecs_entity_t src_entity,
|
||||
ecs_table_t *new_table,
|
||||
int32_t new_index,
|
||||
ecs_table_t *old_table,
|
||||
int32_t old_index,
|
||||
bool construct);
|
||||
|
||||
/* Grow table with specified number of records. Populate table with entities,
|
||||
* starting from specified entity id. */
|
||||
int32_t flecs_table_appendn(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_data_t *data,
|
||||
int32_t count,
|
||||
const ecs_entity_t *ids);
|
||||
|
||||
/* Set table to a fixed size. Useful for preallocating memory in advance. */
|
||||
void flecs_table_set_size(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_data_t *data,
|
||||
int32_t count);
|
||||
|
||||
/* Shrink table to contents */
|
||||
bool flecs_table_shrink(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Get dirty state for table columns */
|
||||
int32_t* flecs_table_get_dirty_state(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Initialize root table */
|
||||
void flecs_init_root_table(
|
||||
ecs_world_t *world);
|
||||
|
||||
/* Unset components in table */
|
||||
void flecs_table_remove_actions(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Free table */
|
||||
void flecs_table_free(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Free table */
|
||||
void flecs_table_free_type(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Replace data */
|
||||
void flecs_table_replace_data(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_data_t *data);
|
||||
|
||||
/* Merge data of one table into another table */
|
||||
void flecs_table_merge(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *new_table,
|
||||
ecs_table_t *old_table,
|
||||
ecs_data_t *new_data,
|
||||
ecs_data_t *old_data);
|
||||
|
||||
void flecs_table_swap(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
int32_t row_1,
|
||||
int32_t row_2);
|
||||
|
||||
void flecs_table_mark_dirty(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_entity_t component);
|
||||
|
||||
void flecs_table_notify(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_table_event_t *event);
|
||||
|
||||
void flecs_table_delete_entities(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
int32_t flecs_table_column_to_union_index(
|
||||
const ecs_table_t *table,
|
||||
int32_t column);
|
||||
|
||||
/* Increase observer count of table */
|
||||
void flecs_table_traversable_add(
|
||||
ecs_table_t *table,
|
||||
int32_t value);
|
||||
|
||||
#endif
|
||||
282
engine/libs/flecs/src/storage/table_cache.c
Normal file
282
engine/libs/flecs/src/storage/table_cache.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* @file table_cache.c
|
||||
* @brief Data structure for fast table iteration/lookups.
|
||||
*
|
||||
* A table cache is a data structure that provides constant time operations for
|
||||
* insertion and removal of tables, and to testing whether a table is registered
|
||||
* with the cache. A table cache also provides functions to iterate the tables
|
||||
* in a cache.
|
||||
*
|
||||
* The world stores a table cache per (component) id inside the id record
|
||||
* administration. Cached queries store a table cache with matched tables.
|
||||
*
|
||||
* A table cache has separate lists for non-empty tables and empty tables. This
|
||||
* improves performance as applications don't waste time iterating empty tables.
|
||||
*/
|
||||
|
||||
#include "../private_api.h"
|
||||
|
||||
static
|
||||
void flecs_table_cache_list_remove(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_hdr_t *elem)
|
||||
{
|
||||
ecs_table_cache_hdr_t *next = elem->next;
|
||||
ecs_table_cache_hdr_t *prev = elem->prev;
|
||||
|
||||
if (next) {
|
||||
next->prev = prev;
|
||||
}
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
cache->empty_tables.count -= !!elem->empty;
|
||||
cache->tables.count -= !elem->empty;
|
||||
|
||||
if (cache->empty_tables.first == elem) {
|
||||
cache->empty_tables.first = next;
|
||||
} else if (cache->tables.first == elem) {
|
||||
cache->tables.first = next;
|
||||
}
|
||||
if (cache->empty_tables.last == elem) {
|
||||
cache->empty_tables.last = prev;
|
||||
}
|
||||
if (cache->tables.last == elem) {
|
||||
cache->tables.last = prev;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void flecs_table_cache_list_insert(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_hdr_t *elem)
|
||||
{
|
||||
ecs_table_cache_hdr_t *last;
|
||||
if (elem->empty) {
|
||||
last = cache->empty_tables.last;
|
||||
cache->empty_tables.last = elem;
|
||||
if ((++ cache->empty_tables.count) == 1) {
|
||||
cache->empty_tables.first = elem;
|
||||
}
|
||||
} else {
|
||||
last = cache->tables.last;
|
||||
cache->tables.last = elem;
|
||||
if ((++ cache->tables.count) == 1) {
|
||||
cache->tables.first = elem;
|
||||
}
|
||||
}
|
||||
|
||||
elem->next = NULL;
|
||||
elem->prev = last;
|
||||
|
||||
if (last) {
|
||||
last->next = elem;
|
||||
}
|
||||
}
|
||||
|
||||
void ecs_table_cache_init(
|
||||
ecs_world_t *world,
|
||||
ecs_table_cache_t *cache)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_map_init_w_params(&cache->index, &world->allocators.ptr);
|
||||
}
|
||||
|
||||
void ecs_table_cache_fini(
|
||||
ecs_table_cache_t *cache)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_map_fini(&cache->index);
|
||||
}
|
||||
|
||||
bool ecs_table_cache_is_empty(
|
||||
const ecs_table_cache_t *cache)
|
||||
{
|
||||
return ecs_map_count(&cache->index) == 0;
|
||||
}
|
||||
|
||||
void ecs_table_cache_insert(
|
||||
ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table,
|
||||
ecs_table_cache_hdr_t *result)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(ecs_table_cache_get(cache, table) == NULL,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(result != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
bool empty;
|
||||
if (!table) {
|
||||
empty = false;
|
||||
} else {
|
||||
empty = ecs_table_count(table) == 0;
|
||||
}
|
||||
|
||||
result->cache = cache;
|
||||
result->table = ECS_CONST_CAST(ecs_table_t*, table);
|
||||
result->empty = empty;
|
||||
|
||||
flecs_table_cache_list_insert(cache, result);
|
||||
|
||||
if (table) {
|
||||
ecs_map_insert_ptr(&cache->index, table->id, result);
|
||||
}
|
||||
|
||||
ecs_assert(empty || cache->tables.first != NULL,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(!empty || cache->empty_tables.first != NULL,
|
||||
ECS_INTERNAL_ERROR, NULL);
|
||||
}
|
||||
|
||||
void ecs_table_cache_replace(
|
||||
ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table,
|
||||
ecs_table_cache_hdr_t *elem)
|
||||
{
|
||||
ecs_table_cache_hdr_t **r = ecs_map_get_ref(
|
||||
&cache->index, ecs_table_cache_hdr_t, table->id);
|
||||
ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_table_cache_hdr_t *old = *r;
|
||||
ecs_assert(old != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_table_cache_hdr_t *prev = old->prev, *next = old->next;
|
||||
if (prev) {
|
||||
ecs_assert(prev->next == old, ECS_INTERNAL_ERROR, NULL);
|
||||
prev->next = elem;
|
||||
}
|
||||
if (next) {
|
||||
ecs_assert(next->prev == old, ECS_INTERNAL_ERROR, NULL);
|
||||
next->prev = elem;
|
||||
}
|
||||
|
||||
if (cache->empty_tables.first == old) {
|
||||
cache->empty_tables.first = elem;
|
||||
}
|
||||
if (cache->empty_tables.last == old) {
|
||||
cache->empty_tables.last = elem;
|
||||
}
|
||||
if (cache->tables.first == old) {
|
||||
cache->tables.first = elem;
|
||||
}
|
||||
if (cache->tables.last == old) {
|
||||
cache->tables.last = elem;
|
||||
}
|
||||
|
||||
*r = elem;
|
||||
elem->prev = prev;
|
||||
elem->next = next;
|
||||
}
|
||||
|
||||
void* ecs_table_cache_get(
|
||||
const ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
if (table) {
|
||||
if (ecs_map_is_init(&cache->index)) {
|
||||
return ecs_map_get_deref(&cache->index, void**, table->id);
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
ecs_table_cache_hdr_t *elem = cache->tables.first;
|
||||
ecs_assert(!elem || elem->table == NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
void* ecs_table_cache_remove(
|
||||
ecs_table_cache_t *cache,
|
||||
uint64_t table_id,
|
||||
ecs_table_cache_hdr_t *elem)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(table_id != 0, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_assert(elem->cache == cache, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
flecs_table_cache_list_remove(cache, elem);
|
||||
ecs_map_remove(&cache->index, table_id);
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
bool ecs_table_cache_set_empty(
|
||||
ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table,
|
||||
bool empty)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
|
||||
ecs_table_cache_hdr_t *elem = ecs_map_get_deref(&cache->index,
|
||||
ecs_table_cache_hdr_t, table->id);
|
||||
if (!elem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elem->empty == empty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
flecs_table_cache_list_remove(cache, elem);
|
||||
elem->empty = empty;
|
||||
flecs_table_cache_list_insert(cache, elem);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flecs_table_cache_iter(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_iter_t *out)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
out->next = cache->tables.first;
|
||||
out->next_list = NULL;
|
||||
out->cur = NULL;
|
||||
return out->next != NULL;
|
||||
}
|
||||
|
||||
bool flecs_table_cache_empty_iter(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_iter_t *out)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
out->next = cache->empty_tables.first;
|
||||
out->next_list = NULL;
|
||||
out->cur = NULL;
|
||||
return out->next != NULL;
|
||||
}
|
||||
|
||||
bool flecs_table_cache_all_iter(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_iter_t *out)
|
||||
{
|
||||
ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
ecs_assert(out != NULL, ECS_INTERNAL_ERROR, NULL);
|
||||
out->next = cache->empty_tables.first;
|
||||
out->next_list = cache->tables.first;
|
||||
out->cur = NULL;
|
||||
return out->next != NULL || out->next_list != NULL;
|
||||
}
|
||||
|
||||
ecs_table_cache_hdr_t* flecs_table_cache_next_(
|
||||
ecs_table_cache_iter_t *it)
|
||||
{
|
||||
ecs_table_cache_hdr_t *next = it->next;
|
||||
if (!next) {
|
||||
next = it->next_list;
|
||||
it->next_list = NULL;
|
||||
if (!next) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
it->cur = next;
|
||||
it->next = next->next;
|
||||
return next;
|
||||
}
|
||||
64
engine/libs/flecs/src/storage/table_cache.h
Normal file
64
engine/libs/flecs/src/storage/table_cache.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @file table_cache.h
|
||||
* @brief Data structure for fast table iteration/lookups.
|
||||
*/
|
||||
|
||||
#ifndef FLECS_TABLE_CACHE_H_
|
||||
#define FLECS_TABLE_CACHE_H_
|
||||
|
||||
void ecs_table_cache_init(
|
||||
ecs_world_t *world,
|
||||
ecs_table_cache_t *cache);
|
||||
|
||||
void ecs_table_cache_fini(
|
||||
ecs_table_cache_t *cache);
|
||||
|
||||
void ecs_table_cache_insert(
|
||||
ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table,
|
||||
ecs_table_cache_hdr_t *result);
|
||||
|
||||
void ecs_table_cache_replace(
|
||||
ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table,
|
||||
ecs_table_cache_hdr_t *elem);
|
||||
|
||||
void* ecs_table_cache_remove(
|
||||
ecs_table_cache_t *cache,
|
||||
uint64_t table_id,
|
||||
ecs_table_cache_hdr_t *elem);
|
||||
|
||||
void* ecs_table_cache_get(
|
||||
const ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table);
|
||||
|
||||
bool ecs_table_cache_set_empty(
|
||||
ecs_table_cache_t *cache,
|
||||
const ecs_table_t *table,
|
||||
bool empty);
|
||||
|
||||
bool ecs_table_cache_is_empty(
|
||||
const ecs_table_cache_t *cache);
|
||||
|
||||
#define flecs_table_cache_count(cache) (cache)->tables.count
|
||||
#define flecs_table_cache_empty_count(cache) (cache)->empty_tables.count
|
||||
|
||||
bool flecs_table_cache_iter(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_iter_t *out);
|
||||
|
||||
bool flecs_table_cache_empty_iter(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_iter_t *out);
|
||||
|
||||
bool flecs_table_cache_all_iter(
|
||||
ecs_table_cache_t *cache,
|
||||
ecs_table_cache_iter_t *out);
|
||||
|
||||
ecs_table_cache_hdr_t* flecs_table_cache_next_(
|
||||
ecs_table_cache_iter_t *it);
|
||||
|
||||
#define flecs_table_cache_next(it, T)\
|
||||
(ECS_CAST(T*, flecs_table_cache_next_(it)))
|
||||
|
||||
#endif
|
||||
1240
engine/libs/flecs/src/storage/table_graph.c
Normal file
1240
engine/libs/flecs/src/storage/table_graph.c
Normal file
File diff suppressed because it is too large
Load Diff
104
engine/libs/flecs/src/storage/table_graph.h
Normal file
104
engine/libs/flecs/src/storage/table_graph.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @file table_graph.h
|
||||
* @brief Table graph types and functions.
|
||||
*/
|
||||
|
||||
#ifndef FLECS_TABLE_GRAPH_H
|
||||
#define FLECS_TABLE_GRAPH_H
|
||||
|
||||
/** Cache of added/removed components for non-trivial edges between tables */
|
||||
#define ECS_TABLE_DIFF_INIT { .added = {0}}
|
||||
|
||||
/** Builder for table diff. The table diff type itself doesn't use ecs_vec_t to
|
||||
* conserve memory on table edges (a type doesn't have the size field), whereas
|
||||
* a vec for the builder is more convenient to use & has allocator support. */
|
||||
typedef struct ecs_table_diff_builder_t {
|
||||
ecs_vec_t added;
|
||||
ecs_vec_t removed;
|
||||
} ecs_table_diff_builder_t;
|
||||
|
||||
typedef struct ecs_table_diff_t {
|
||||
ecs_type_t added; /* Components added between tables */
|
||||
ecs_type_t removed; /* Components removed between tables */
|
||||
} ecs_table_diff_t;
|
||||
|
||||
/** Edge linked list (used to keep track of incoming edges) */
|
||||
typedef struct ecs_graph_edge_hdr_t {
|
||||
struct ecs_graph_edge_hdr_t *prev;
|
||||
struct ecs_graph_edge_hdr_t *next;
|
||||
} ecs_graph_edge_hdr_t;
|
||||
|
||||
/** Single edge. */
|
||||
typedef struct ecs_graph_edge_t {
|
||||
ecs_graph_edge_hdr_t hdr;
|
||||
ecs_table_t *from; /* Edge source table */
|
||||
ecs_table_t *to; /* Edge destination table */
|
||||
ecs_table_diff_t *diff; /* Index into diff vector, if non trivial edge */
|
||||
ecs_id_t id; /* Id associated with edge */
|
||||
} ecs_graph_edge_t;
|
||||
|
||||
/* Edges to other tables. */
|
||||
typedef struct ecs_graph_edges_t {
|
||||
ecs_graph_edge_t *lo; /* Small array optimized for low edges */
|
||||
ecs_map_t *hi; /* Map for hi edges (map<id, edge_t>) */
|
||||
} ecs_graph_edges_t;
|
||||
|
||||
/* Table graph node */
|
||||
typedef struct ecs_graph_node_t {
|
||||
/* Outgoing edges */
|
||||
ecs_graph_edges_t add;
|
||||
ecs_graph_edges_t remove;
|
||||
|
||||
/* Incoming edges (next = add edges, prev = remove edges) */
|
||||
ecs_graph_edge_hdr_t refs;
|
||||
} ecs_graph_node_t;
|
||||
|
||||
/* Find table by adding id to current table */
|
||||
ecs_table_t *flecs_table_traverse_add(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_id_t *id_ptr,
|
||||
ecs_table_diff_t *diff);
|
||||
|
||||
/* Find table by removing id from current table */
|
||||
ecs_table_t *flecs_table_traverse_remove(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table,
|
||||
ecs_id_t *id_ptr,
|
||||
ecs_table_diff_t *diff);
|
||||
|
||||
/* Cleanup incoming and outgoing edges for table */
|
||||
void flecs_table_clear_edges(
|
||||
ecs_world_t *world,
|
||||
ecs_table_t *table);
|
||||
|
||||
/* Table diff builder, used to build id lists that indicate the difference in
|
||||
* ids between two tables. */
|
||||
void flecs_table_diff_builder_init(
|
||||
ecs_world_t *world,
|
||||
ecs_table_diff_builder_t *builder);
|
||||
|
||||
void flecs_table_diff_builder_fini(
|
||||
ecs_world_t *world,
|
||||
ecs_table_diff_builder_t *builder);
|
||||
|
||||
void flecs_table_diff_builder_clear(
|
||||
ecs_table_diff_builder_t *builder);
|
||||
|
||||
void flecs_table_diff_build_append_table(
|
||||
ecs_world_t *world,
|
||||
ecs_table_diff_builder_t *dst,
|
||||
ecs_table_diff_t *src);
|
||||
|
||||
void flecs_table_diff_build(
|
||||
ecs_world_t *world,
|
||||
ecs_table_diff_builder_t *builder,
|
||||
ecs_table_diff_t *diff,
|
||||
int32_t added_offset,
|
||||
int32_t removed_offset);
|
||||
|
||||
void flecs_table_diff_build_noalloc(
|
||||
ecs_table_diff_builder_t *builder,
|
||||
ecs_table_diff_t *diff);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user