Properly link flecs library

This commit is contained in:
2023-11-09 11:38:29 +01:00
parent dc585396c3
commit 8edcf9305c
1392 changed files with 390081 additions and 164 deletions

View 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);
}

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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