[ecs] Add "tree mode" to hierarchies

As I had long suspected, building large hierarchies is fiendishly
expensive (at least O(N^2)). However, this is because the hierarchies
are structured such that adding high-level nodes results in a lot of
copying due to the flattened (breadth-first) layout (which does make for
excellent breadth-first performance when working with a hierarchy).

Using tree mode allows adding new nodes to be O(1) (I guess O(N) for the
size of the sub-tree being added, but that's not supported yet) and
costs only an additional 8 bytes per node. Switching from flat mode to
tree mode is very cheap as only the additional tree-related indices need
to be fixed up (they're almost entirely ignored in flat mode). Switching
from tree to flat mode is a little more expensive as the entire tree
needs to be copied, but it seems to be an O(N) (size of the tree).

With this, building the style editor window went from about 25% to about
5% (and most of that is realloc!), with a 1.3% cost for switching from
tree mode to flat mode.

There's still a lot of work to do (supporting removal and tree inserts).
This commit is contained in:
Bill Currie 2023-07-07 14:42:49 +09:00
parent 008aebf344
commit 0360e33a00
5 changed files with 862 additions and 8 deletions

View file

@ -57,18 +57,24 @@ typedef struct hierarchy_s {
uint32_t *childCount; uint32_t *childCount;
uint32_t *childIndex; uint32_t *childIndex;
uint32_t *parentIndex; uint32_t *parentIndex;
uint32_t *nextIndex;
uint32_t *lastIndex;
const hierarchy_type_t *type; const hierarchy_type_t *type;
void **components; void **components;
struct ecs_registry_s *reg; struct ecs_registry_s *reg;
uint32_t href_comp; uint32_t href_comp;
bool tree_mode; // use for fast building
} hierarchy_t; } hierarchy_t;
#define nullindex (~0u)
hierarchy_t *Hierarchy_New (struct ecs_registry_s *reg, uint32_t href_comp, hierarchy_t *Hierarchy_New (struct ecs_registry_s *reg, uint32_t href_comp,
const hierarchy_type_t *type, int createRoot); const hierarchy_type_t *type, int createRoot);
void Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count); void Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count);
hierarchy_t *Hierarchy_Copy (struct ecs_registry_s *reg, uint32_t href_comp, hierarchy_t *Hierarchy_Copy (struct ecs_registry_s *reg, uint32_t href_comp,
const hierarchy_t *src); const hierarchy_t *src);
void Hierarchy_Delete (hierarchy_t *hierarchy); void Hierarchy_Delete (hierarchy_t *hierarchy);
void Hierarchy_SetTreeMode (hierarchy_t *hierarchy, bool tree_mode);
uint32_t Hierarchy_InsertHierarchy (hierarchy_t *dst, const hierarchy_t *src, uint32_t Hierarchy_InsertHierarchy (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstParent, uint32_t srcRoot); uint32_t dstParent, uint32_t srcRoot);

View file

@ -39,12 +39,14 @@
#include "QF/ecs.h" #include "QF/ecs.h"
#define nullindex (~0u)
static component_t ent_component = { .size = sizeof (uint32_t) }; static component_t ent_component = { .size = sizeof (uint32_t) };
static component_t childCount_component = { .size = sizeof (uint32_t) }; static component_t childCount_component = { .size = sizeof (uint32_t) };
static component_t childIndex_component = { .size = sizeof (uint32_t) }; static component_t childIndex_component = { .size = sizeof (uint32_t) };
static component_t parentIndex_component = { .size = sizeof (uint32_t) }; static component_t parentIndex_component = { .size = sizeof (uint32_t) };
static component_t nextIndex_component = { .size = sizeof (uint32_t) };
static component_t lastIndex_component = { .size = sizeof (uint32_t) };
static void static void
hierarchy_UpdateTransformIndices (hierarchy_t *hierarchy, uint32_t start, hierarchy_UpdateTransformIndices (hierarchy_t *hierarchy, uint32_t start,
@ -109,6 +111,10 @@ Hierarchy_Reserve (hierarchy_t *hierarchy, uint32_t count)
(void **) &hierarchy->childIndex, new_max); (void **) &hierarchy->childIndex, new_max);
Component_ResizeArray (&parentIndex_component, Component_ResizeArray (&parentIndex_component,
(void **) &hierarchy->parentIndex, new_max); (void **) &hierarchy->parentIndex, new_max);
Component_ResizeArray (&nextIndex_component,
(void **) &hierarchy->nextIndex, new_max);
Component_ResizeArray (&lastIndex_component,
(void **) &hierarchy->lastIndex, new_max);
if (hierarchy->type) { if (hierarchy->type) {
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
@ -128,6 +134,9 @@ hierarchy_open (hierarchy_t *hierarchy, uint32_t index, uint32_t count)
hierarchy->num_objects += count; hierarchy->num_objects += count;
uint32_t dstIndex = index + count; uint32_t dstIndex = index + count;
count = hierarchy->num_objects - index - count; count = hierarchy->num_objects - index - count;
if (!count) {
return;
}
Component_MoveElements (&ent_component, Component_MoveElements (&ent_component,
hierarchy->ent, dstIndex, index, count); hierarchy->ent, dstIndex, index, count);
Component_MoveElements (&childCount_component, Component_MoveElements (&childCount_component,
@ -136,6 +145,10 @@ hierarchy_open (hierarchy_t *hierarchy, uint32_t index, uint32_t count)
hierarchy->childIndex, dstIndex, index, count); hierarchy->childIndex, dstIndex, index, count);
Component_MoveElements (&parentIndex_component, Component_MoveElements (&parentIndex_component,
hierarchy->parentIndex, dstIndex, index, count); hierarchy->parentIndex, dstIndex, index, count);
Component_MoveElements (&nextIndex_component,
hierarchy->nextIndex, dstIndex, index, count);
Component_MoveElements (&lastIndex_component,
hierarchy->lastIndex, dstIndex, index, count);
if (hierarchy->type) { if (hierarchy->type) {
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
Component_MoveElements (&hierarchy->type->components[i], Component_MoveElements (&hierarchy->type->components[i],
@ -162,6 +175,10 @@ hierarchy_close (hierarchy_t *hierarchy, uint32_t index, uint32_t count)
hierarchy->childIndex, index, srcIndex, count); hierarchy->childIndex, index, srcIndex, count);
Component_MoveElements (&parentIndex_component, Component_MoveElements (&parentIndex_component,
hierarchy->parentIndex, index, srcIndex, count); hierarchy->parentIndex, index, srcIndex, count);
Component_MoveElements (&nextIndex_component,
hierarchy->nextIndex, index, srcIndex, count);
Component_MoveElements (&lastIndex_component,
hierarchy->lastIndex, index, srcIndex, count);
if (hierarchy->type) { if (hierarchy->type) {
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
Component_MoveElements (&hierarchy->type->components[i], Component_MoveElements (&hierarchy->type->components[i],
@ -212,6 +229,7 @@ hierarchy_init (hierarchy_t *dst, uint32_t index,
dst->parentIndex[index + i] = parentIndex; dst->parentIndex[index + i] = parentIndex;
dst->childCount[index + i] = 0; dst->childCount[index + i] = 0;
dst->childIndex[index + i] = childIndex; dst->childIndex[index + i] = childIndex;
dst->lastIndex[index + i] = nullindex;
} }
if (dst->type) { if (dst->type) {
for (uint32_t i = 0; i < dst->type->num_components; i++) { for (uint32_t i = 0; i < dst->type->num_components; i++) {
@ -222,8 +240,8 @@ hierarchy_init (hierarchy_t *dst, uint32_t index,
} }
static uint32_t static uint32_t
hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src, hierarchy_insert_flat (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstParent, uint32_t *srcRoot, uint32_t count) uint32_t dstParent, uint32_t *srcRoot, uint32_t count)
{ {
uint32_t insertIndex; // where the objects will be inserted uint32_t insertIndex; // where the objects will be inserted
uint32_t childIndex; // where the objects' children will inserted uint32_t childIndex; // where the objects' children will inserted
@ -273,6 +291,62 @@ hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src,
return insertIndex; return insertIndex;
} }
static uint32_t
hierarchy_insert_tree (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstParent, uint32_t *srcRoot, uint32_t count)
{
uint32_t insertIndex;
if (dst == src) {
// reparenting within the hierarchy, so need only to update indices
// of course, easier said than done
insertIndex = *srcRoot;
uint32_t srcParent = dst->parentIndex[insertIndex];
uint32_t *next = &dst->nextIndex[srcParent];
while (*next != insertIndex) {
next = &dst->nextIndex[*next];
}
if (dst->lastIndex[srcParent] == insertIndex) {
// removing src from the end of srcParent's child chain
dst->lastIndex[srcParent] = next - dst->nextIndex;
}
*next = dst->nextIndex[insertIndex];
dst->nextIndex[insertIndex] = nullindex;
dst->nextIndex[dst->lastIndex[dstParent]] = insertIndex;
dst->lastIndex[dstParent] = insertIndex;
} else {
// new objecs are always appended
insertIndex = dst->num_objects;
hierarchy_open (dst, insertIndex, count);
if (dst->childCount[dstParent]) {
dst->nextIndex[dst->lastIndex[dstParent]] = insertIndex;
} else {
dst->childIndex[dstParent] = insertIndex;
}
dst->childCount[dstParent] += count;
dst->lastIndex[dstParent] = insertIndex;
dst->nextIndex[insertIndex] = nullindex;
if (src) {
hierarchy_move (dst, src, insertIndex, *srcRoot, count);
} else {
hierarchy_init (dst, insertIndex, dstParent, nullindex, count);
}
}
return insertIndex;
}
static uint32_t
hierarchy_insert (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstParent, uint32_t *srcRoot, uint32_t count)
{
if (dst->tree_mode) {
return hierarchy_insert_tree (dst, src, dstParent, srcRoot, count);
} else {
return hierarchy_insert_flat (dst, src, dstParent, srcRoot, count);
}
}
static void static void
hierarchy_insert_children (hierarchy_t *dst, const hierarchy_t *src, hierarchy_insert_children (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstParent, uint32_t *srcRoot) uint32_t dstParent, uint32_t *srcRoot)
@ -363,6 +437,9 @@ void
Hierarchy_RemoveHierarchy (hierarchy_t *hierarchy, uint32_t index, Hierarchy_RemoveHierarchy (hierarchy_t *hierarchy, uint32_t index,
int delEntities) int delEntities)
{ {
if (hierarchy->tree_mode) {
Sys_Error ("Hierarchy_RemoveHierarchy tree mode not implemented");
}
uint32_t parentIndex = hierarchy->parentIndex[index]; uint32_t parentIndex = hierarchy->parentIndex[index];
hierarchy_remove_children (hierarchy, index, delEntities); hierarchy_remove_children (hierarchy, index, delEntities);
@ -402,14 +479,15 @@ Hierarchy_New (ecs_registry_t *reg, uint32_t href_comp,
return hierarchy; return hierarchy;
} }
void static void
Hierarchy_Delete (hierarchy_t *hierarchy) hierarchy_delete (hierarchy_t *hierarchy)
{ {
hierarchy_InvalidateReferences (hierarchy, 0, hierarchy->num_objects);
free (hierarchy->ent); free (hierarchy->ent);
free (hierarchy->childCount); free (hierarchy->childCount);
free (hierarchy->childIndex); free (hierarchy->childIndex);
free (hierarchy->parentIndex); free (hierarchy->parentIndex);
free (hierarchy->nextIndex);
free (hierarchy->lastIndex);
if (hierarchy->type) { if (hierarchy->type) {
for (uint32_t i = 0; i < hierarchy->type->num_components; i++) { for (uint32_t i = 0; i < hierarchy->type->num_components; i++) {
free (hierarchy->components[i]); free (hierarchy->components[i]);
@ -421,10 +499,138 @@ Hierarchy_Delete (hierarchy_t *hierarchy)
PR_RESFREE (reg->hierarchies, hierarchy); PR_RESFREE (reg->hierarchies, hierarchy);
} }
void
Hierarchy_Delete (hierarchy_t *hierarchy)
{
hierarchy_InvalidateReferences (hierarchy, 0, hierarchy->num_objects);
hierarchy_delete (hierarchy);
}
static uint32_t
copy_one_node (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstIndex, uint32_t childIndex)
{
uint32_t srcIndex = dst->parentIndex[dstIndex];
uint32_t childCount = src->childCount[srcIndex];
dst->childIndex[dstIndex] = childIndex;
dst->childCount[dstIndex] = childCount;
dst->ent[dstIndex] = src->ent[srcIndex];
if (dst->type) {
for (uint32_t i = 0; i < dst->type->num_components; i++) {
Component_CopyElements (&dst->type->components[i],
dst->components[i], dstIndex,
src->components[i], srcIndex, 1);
}
}
return srcIndex;
}
static uint32_t
queue_tree_nodes (hierarchy_t *dst, const hierarchy_t *src,
uint32_t queueIndex, uint32_t srcIndex)
{
uint32_t srcChild = src->childIndex[srcIndex];
uint32_t childCount = src->childCount[srcIndex];
for (uint32_t i = 0; i < childCount; i++) {
dst->parentIndex[queueIndex + i] = srcChild;
srcChild = src->nextIndex[srcChild];
}
return childCount;
}
static void
copy_tree_nodes (hierarchy_t *dst, const hierarchy_t *src,
uint32_t dstIndex, uint32_t *queueIndex)
{
auto ind = copy_one_node (dst, src, dstIndex, *queueIndex);
auto count = queue_tree_nodes (dst, src, *queueIndex, ind);
*queueIndex += count;
}
static void
swap_pointers (void *a, void *b)
{
void *t = *(void **)a;
*(void **)a = *(void **) b;
*(void **)b = t;
}
void
Hierarchy_SetTreeMode (hierarchy_t *hierarchy, bool tree_mode)
{
if (!hierarchy->tree_mode == !tree_mode) {
// no change
return;
}
hierarchy->tree_mode = tree_mode;
if (tree_mode) {
// switching from a cononical hierarchy to tree mode, noed only to
// ensure next/last indices are correct
// root node has no siblings
hierarchy->nextIndex[0] = nullindex;
for (uint32_t i = 0; i < hierarchy->num_objects; i++) {
uint32_t count = hierarchy->childCount[i];
uint32_t child = hierarchy->childIndex[i];
for (uint32_t j = 0; count && j < count - 1; j++) {
hierarchy->nextIndex[child + j] = child + j + 1;
}
hierarchy->lastIndex[i] = count ? child + count - 1 : nullindex;
if (count) {
hierarchy->nextIndex[hierarchy->lastIndex[i]] = nullindex;
} else {
hierarchy->childIndex[i] = nullindex;
}
}
return;
}
auto src = hierarchy;
auto tmp = Hierarchy_New (src->reg, src->href_comp, src->type, 0);
Hierarchy_Reserve (tmp, src->num_objects);
tmp->num_objects = src->num_objects;
// treat parentIndex as a queue for breadth-first traversal
tmp->parentIndex[0] = 0; // start at root of src
uint32_t queueIndex = 1;
for (uint32_t i = 0; i < src->num_objects; i++) {
copy_tree_nodes (tmp, src, i, &queueIndex);
}
tmp->parentIndex[0] = nullindex;
for (uint32_t i = 0; i < src->num_objects; i++) {
for (uint32_t j = 0; j < tmp->childCount[i]; j++) {
tmp->parentIndex[tmp->childIndex[i] + j] = i;
}
}
auto href_comp = src->href_comp;
for (uint32_t i = 0; i < src->num_objects; i++) {
hierref_t *ref = Ent_GetComponent (tmp->ent[i], href_comp, tmp->reg);
ref->index = i;
}
swap_pointers (&tmp->ent, &src->ent);
swap_pointers (&tmp->childCount, &src->childCount);
swap_pointers (&tmp->childIndex, &src->childIndex);
swap_pointers (&tmp->parentIndex, &src->parentIndex);
swap_pointers (&tmp->nextIndex, &src->nextIndex);
swap_pointers (&tmp->lastIndex, &src->lastIndex);
if (src->type) {
for (uint32_t i = 0; i < src->type->num_components; i++) {
swap_pointers (&tmp->components[i], &src->components[i]);
}
}
hierarchy_delete (tmp);
}
hierarchy_t * hierarchy_t *
Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp, Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp,
const hierarchy_t *src) const hierarchy_t *src)
{ {
if (src->tree_mode) {
Sys_Error ("Hierarchy_Copy tree mode not implemented");
}
hierarchy_t *dst = Hierarchy_New (dstReg, href_comp, src->type, 0); hierarchy_t *dst = Hierarchy_New (dstReg, href_comp, src->type, 0);
size_t count = src->num_objects; size_t count = src->num_objects;
@ -443,6 +649,10 @@ Hierarchy_Copy (ecs_registry_t *dstReg, uint32_t href_comp,
dst->childIndex, 0, src->childIndex, 0, count); dst->childIndex, 0, src->childIndex, 0, count);
Component_CopyElements (&parentIndex_component, Component_CopyElements (&parentIndex_component,
dst->parentIndex, 0, src->parentIndex, 0, count); dst->parentIndex, 0, src->parentIndex, 0, count);
Component_CopyElements (&nextIndex_component,
dst->nextIndex, 0, src->nextIndex, 0, count);
Component_CopyElements (&lastIndex_component,
dst->lastIndex, 0, src->lastIndex, 0, count);
if (dst->type) { if (dst->type) {
for (uint32_t i = 0; i < dst->type->num_components; i++) { for (uint32_t i = 0; i < dst->type->num_components; i++) {
Component_CopyElements (&dst->type->components[i], Component_CopyElements (&dst->type->components[i],
@ -457,6 +667,9 @@ hierref_t
Hierarchy_SetParent (hierarchy_t *dst, uint32_t dstParent, Hierarchy_SetParent (hierarchy_t *dst, uint32_t dstParent,
hierarchy_t *src, uint32_t srcRoot) hierarchy_t *src, uint32_t srcRoot)
{ {
if (src->tree_mode) {
Sys_Error ("Hierarchy_SetParent tree mode not implemented");
}
hierref_t r = {}; hierref_t r = {};
if (dst && dstParent != nullindex) { if (dst && dstParent != nullindex) {
if (dst->type != src->type) { if (dst->type != src->type) {

View file

@ -3,7 +3,8 @@ libs_ecs_tests = \
libs/ecs/test/test-compops \ libs/ecs/test/test-compops \
libs/ecs/test/test-hierarchy \ libs/ecs/test/test-hierarchy \
libs/ecs/test/test-registry \ libs/ecs/test/test-registry \
libs/ecs/test/test-subpools libs/ecs/test/test-subpools \
libs/ecs/test/test-treehierarchy
TESTS += $(libs_ecs_tests) TESTS += $(libs_ecs_tests)
@ -47,3 +48,10 @@ libs_ecs_test_test_subpools_LDADD= \
$(libs_ecs_test_libs) $(libs_ecs_test_libs)
libs_ecs_test_test_subpools_DEPENDENCIES= \ libs_ecs_test_test_subpools_DEPENDENCIES= \
$(libs_ecs_test_libs) $(libs_ecs_test_libs)
libs_ecs_test_test_treehierarchy_SOURCES= \
libs/ecs/test/test-treehierarchy.c
libs_ecs_test_test_treehierarchy_LDADD= \
$(libs_ecs_test_libs)
libs_ecs_test_test_treehierarchy_DEPENDENCIES= \
$(libs_ecs_test_libs)

View file

@ -0,0 +1,623 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "QF/ecs.h"
enum {
test_href,
test_name,
test_highlight,
test_num_components
};
static const component_t test_components[] = {
[test_href] = {
.size = sizeof (hierref_t),
.create = 0,//create_href,
.name = "href",
.destroy = Hierref_DestroyComponent,
},
[test_name] = {
.size = sizeof (const char *),
.name = "name",
},
[test_highlight] = {
.size = sizeof (byte),
.name = "highlight",
},
};
ecs_registry_t *test_reg;
#define DFL "\e[39;49m"
#define BLK "\e[30;40m"
#define RED "\e[31;40m"
#define GRN "\e[32;40m"
#define ONG "\e[33;40m"
#define BLU "\e[34;40m"
#define MAG "\e[35;40m"
#define CYN "\e[36;40m"
#define WHT "\e[37;40m"
static int
check_hierarchy_size (hierarchy_t *h, uint32_t size)
{
if (h->num_objects != size) {
printf ("hierarchy does not have exactly %u transform\n", size);
return 0;
}
ecs_registry_t *reg = h->reg;
for (uint32_t i = 0; i < h->num_objects; i++) {
hierref_t *ref = Ent_GetComponent (h->ent[i], test_href, reg);
char **name = Ent_GetComponent (h->ent[i], test_name, reg);;
if (ref->hierarchy != h) {
printf ("transform %d (%s) does not point to hierarchy\n",
i, *name);
}
}
return 1;
}
static const char *
ref_index_color (uint32_t i, uint32_t rind)
{
return rind != i ? RED : DFL;
}
static const char *
parent_index_color (hierarchy_t *h, uint32_t i)
{
if (!i && h->parentIndex[i] == nullindex) {
return GRN;
}
if (h->parentIndex[i] >= i) {
return RED;
}
uint32_t ci = h->childIndex[h->parentIndex[i]];
uint32_t cc = h->childCount[h->parentIndex[i]];
if (i < ci || i >= ci + cc) {
return ONG;
}
return DFL;
}
static const char *
child_index_color (hierarchy_t *h, uint32_t i)
{
if (h->tree_mode) {
if ((h->childCount[i] && h->childIndex[i] == nullindex)
|| (!h->childCount[i] && h->childIndex[i] != nullindex)) {
return RED;
}
if (h->childIndex[i] != nullindex
&& h->childIndex[i] >= h->num_objects) {
return RED;
}
} else {
if (h->childIndex[i] > h->num_objects
|| h->childCount[i] > h->num_objects
|| h->childIndex[i] + h->childCount[i] > h->num_objects) {
return RED;
}
if (h->childIndex[i] <= i) {
return ONG;
}
}
return DFL;
}
static const char *
child_count_color (hierarchy_t *h, uint32_t i)
{
if (h->childCount[i] > h->num_objects) {
return RED;
}
if (h->tree_mode) {
if ((h->childCount[i] && h->childIndex[i] == nullindex)
|| (!h->childCount[i] && h->childIndex[i] != nullindex)) {
return RED;
}
} else {
if (h->childIndex[i] > h->num_objects
|| h->childIndex[i] + h->childCount[i] > h->num_objects) {
return RED;
}
}
return DFL;
}
static bool
check_for_loops (hierarchy_t *h)
{
for (uint32_t i = 0; i < h->num_objects; i++) {
if (!h->childCount[i]) {
continue;
}
uint32_t n, c;
for (n = h->childIndex[i], c = h->childCount[i];
n != nullindex; n = h->nextIndex[n], c--) {
if (!c) {
break;
}
}
if (!c && n != nullindex) {
printf ("too many children at %d\n", i);
return false;
}
if (c && n == nullindex) {
printf ("too few children at %d\n", i);
return false;
}
if (c && n != nullindex) {
printf ("what the what?!? at %d\n", i);
return false;
}
}
return true;
}
static bool
check_next_index (hierarchy_t *h, uint32_t i)
{
if (i == 0) {
// root never has siblings
if (h->nextIndex[i] != nullindex) {
return false;
}
return true;
}
uint32_t p;
if ((p = h->parentIndex[i]) >= h->num_objects
|| h->childIndex[p] > h->num_objects
|| h->childCount[p] > h->num_objects
|| h->childIndex[p] + h->childCount[p] > h->num_objects
|| h->lastIndex[p] >= h->num_objects
|| (h->nextIndex[i] == nullindex && h->lastIndex[p] != i)
|| (h->nextIndex[i] != nullindex && h->lastIndex[p] == i)) {
return false;
}
if (h->nextIndex[i] != nullindex && h->parentIndex[h->nextIndex[i]] != p) {
return false;
}
return true;
}
static const char *
next_index_color (hierarchy_t *h, uint32_t i)
{
return check_next_index (h, i) ? DFL : RED;
}
static bool
check_last_index (hierarchy_t *h, uint32_t i)
{
if ((h->childCount[i] && h->childIndex[i] >= h->num_objects)
|| h->childCount[i] >= h->num_objects
|| (h->childCount[i] && h->lastIndex[i] == nullindex)
|| (!h->childCount[i] && h->lastIndex[i] != nullindex)) {
return false;
}
if (h->lastIndex[i] != nullindex && h->parentIndex[h->lastIndex[i]] != i) {
return false;
}
return true;
}
static const char *
last_index_color (hierarchy_t *h, uint32_t i)
{
return check_last_index (h, i) ? DFL : RED;
}
static const char *
entity_color (hierarchy_t *h, uint32_t i)
{
return h->ent[i] == nullent ? MAG : DFL;
}
static const char *
highlight_color (hierarchy_t *h, uint32_t i)
{
uint32_t ent = h->ent[i];
if (ECS_EntValid (ent, test_reg)
&& Ent_HasComponent (ent, test_highlight, test_reg)) {
static char color_str[] = "\e[3.;4.m";
byte *color = Ent_GetComponent (ent, test_highlight, test_reg);
if (*color) {
byte fg = *color & 0x0f;
byte bg = *color >> 4;
color_str[3] = fg < 8 ? '0' + fg : '9';
color_str[6] = bg < 8 ? '0' + bg : '9';
return color_str;
}
}
return "";
}
static void
print_header (hierarchy_t *h)
{
if (h->tree_mode) {
puts ("in: ri pa ci cc ni li en|name");
} else {
puts ("in: ri pa ci cc en|name");
}
}
static void
print_line (hierarchy_t *h, uint32_t ind, int level)
{
ecs_registry_t *reg = h->reg;
uint32_t rind = nullindex;
static char fake_name[] = ONG "null" DFL;
static char *fake_nameptr = fake_name;
char **name = &fake_nameptr;
if (ECS_EntValid (h->ent[ind], reg)) {
hierref_t *ref = Ent_GetComponent (h->ent[ind], test_href, reg);
rind = ref->index;
if (Ent_HasComponent (h->ent[ind], test_name, reg)) {
name = Ent_GetComponent (h->ent[ind], test_name, reg);
}
}
printf ("%2d: %s%2d %s%2d %s%2d %s%2d", ind,
ref_index_color (ind, rind), rind,
parent_index_color (h, ind), h->parentIndex[ind],
child_index_color (h, ind), h->childIndex[ind],
child_count_color (h, ind), h->childCount[ind]);
if (h->tree_mode) {
printf (" %s%2d %s%2d",
next_index_color (h, ind), h->nextIndex[ind],
last_index_color (h, ind), h->lastIndex[ind]);
}
printf (" %s%2d"DFL"|%*s%s%s"DFL"\n",
entity_color (h, ind), h->ent[ind],
level * 3, "", highlight_color (h, ind), *name);
}
static void
dump_hierarchy (hierarchy_t *h)
{
print_header (h);
for (uint32_t i = 0; i < h->num_objects; i++) {
print_line (h, i, 0);
}
puts ("");
}
static void
dump_tree (hierarchy_t *h, uint32_t ind, int level)
{
if (ind >= h->num_objects) {
printf ("index %d out of bounds (%d)\n", ind, h->num_objects);
return;
}
if (!level) {
print_header (h);
}
print_line (h, ind, level);
if (h->tree_mode) {
uint32_t count = h->childCount[ind];
uint32_t child;
for (child = h->childIndex[ind]; count && child != nullindex;
child = h->nextIndex[child], count--) {
dump_tree (h, child, level + 1);
}
} else {
if (h->childIndex[ind] > ind) {
for (uint32_t i = 0; i < h->childCount[ind]; i++) {
if (h->childIndex[ind] + i >= h->num_objects) {
break;
}
dump_tree (h, h->childIndex[ind] + i, level + 1);
}
}
}
if (!level) {
puts ("");
}
}
static int
check_indices (uint32_t ent, uint32_t index, uint32_t parentIndex,
uint32_t childIndex, uint32_t childCount)
{
ecs_registry_t *reg = test_reg;
char **entname = Ent_GetComponent (ent, test_name, reg);;
hierref_t *ref = Ent_GetComponent (ent, test_href, reg);
hierarchy_t *h = ref->hierarchy;
if (ref->index != index) {
char **name = Ent_GetComponent (h->ent[index], test_name, reg);;
printf ("%s/%s index incorrect: expect %u got %u\n",
*entname, *name,
index, ref->index);
return 0;
}
if (h->parentIndex[index] != parentIndex) {
printf ("%s parent index incorrect: expect %u got %u\n",
*entname, parentIndex, h->parentIndex[index]);
return 0;
}
if (h->childIndex[index] != childIndex) {
printf ("%s child index incorrect: expect %u got %u\n",
*entname, childIndex, h->childIndex[index]);
return 0;
}
if (h->childCount[index] != childCount) {
printf ("%s child count incorrect: expect %u got %u\n",
*entname, childCount, h->childCount[index]);
return 0;
}
return 1;
}
static bool
check_next_last_indices (hierarchy_t *h)
{
for (uint32_t i = 0; i < h->num_objects; i++) {
if (!check_next_index (h, i)) {
printf ("incorrect next index at %d: %d\n", i, h->nextIndex[i]);
return false;
}
if (!check_last_index (h, i)) {
printf ("incorrect last index at %d: %d\n", i, h->lastIndex[i]);
return false;
}
}
return true;
}
static uint32_t
create_ent (uint32_t parent, const char *name)
{
uint32_t ent = ECS_NewEntity (test_reg);
Ent_SetComponent (ent, test_name, test_reg, &name);
hierref_t *ref = Ent_AddComponent (ent, test_href, test_reg);
if (parent != nullindex) {
hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg);
ref->hierarchy = pref->hierarchy;
ref->index = Hierarchy_InsertHierarchy (pref->hierarchy, 0,
pref->index, 0);
} else {
ref->hierarchy = Hierarchy_New (test_reg, test_href, 0, 1);
ref->index = 0;
}
ref->hierarchy->ent[ref->index] = ent;
return ent;
}
#if 0
static void
highlight_ent (uint32_t ent, byte color)
{
Ent_SetComponent (ent, test_highlight, test_reg, &color);
}
static void
set_parent (uint32_t child, uint32_t parent)
{
if (parent != nullindex) {
hierref_t *pref = Ent_GetComponent (parent, test_href, test_reg);
hierref_t *cref = Ent_GetComponent (child, test_href, test_reg);
Hierarchy_SetParent (pref->hierarchy, pref->index,
cref->hierarchy, cref->index);
} else {
hierref_t *cref = Ent_GetComponent (child, test_href, test_reg);
Hierarchy_SetParent (0, nullindex, cref->hierarchy, cref->index);
}
}
#endif
static int
test_build_hierarchy (void)
{
printf ("test_build_hierarchy\n");
uint32_t root = create_ent (nullent, "root");
uint32_t A = create_ent (root, "A");
uint32_t B = create_ent (root, "B");
uint32_t C = create_ent (root, "C");
hierref_t *ref = Ent_GetComponent (root, test_href, test_reg);
if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; }
if (!check_indices (A, 1, 0, 4, 0)) { return 1; }
if (!check_indices (B, 2, 0, 4, 0)) { return 1; }
if (!check_indices (C, 3, 0, 4, 0)) { return 1; }
uint32_t B1 = create_ent (B, "B1");
if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; }
if (!check_indices ( A, 1, 0, 4, 0)) { return 1; }
if (!check_indices ( B, 2, 0, 4, 1)) { return 1; }
if (!check_indices ( C, 3, 0, 5, 0)) { return 1; }
if (!check_indices (B1, 4, 2, 5, 0)) { return 1; }
uint32_t A1 = create_ent (A, "A1");
if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; }
if (!check_indices ( A, 1, 0, 4, 1)) { return 1; }
if (!check_indices ( B, 2, 0, 5, 1)) { return 1; }
if (!check_indices ( C, 3, 0, 6, 0)) { return 1; }
if (!check_indices (A1, 4, 1, 6, 0)) { return 1; }
if (!check_indices (B1, 5, 2, 6, 0)) { return 1; }
uint32_t A1a = create_ent (A1, "A1a");
uint32_t B2 = create_ent (B, "B2");
uint32_t A2 = create_ent (A, "A2");
uint32_t B3 = create_ent (B, "B3");
uint32_t B2a = create_ent (B2, "B2a");
if (!check_hierarchy_size (ref->hierarchy, 11)) { return 1; }
if (!check_indices (root, 0, nullindex, 1, 3)) { return 1; }
if (!check_indices ( A, 1, 0, 4, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 6, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 9, 0)) { return 1; }
if (!check_indices ( A1, 4, 1, 9, 1)) { return 1; }
if (!check_indices ( A2, 5, 1, 10, 0)) { return 1; }
if (!check_indices ( B1, 6, 2, 10, 0)) { return 1; }
if (!check_indices ( B2, 7, 2, 10, 1)) { return 1; }
if (!check_indices ( B3, 8, 2, 11, 0)) { return 1; }
if (!check_indices (A1a, 9, 4, 11, 0)) { return 1; }
if (!check_indices (B2a, 10, 7, 11, 0)) { return 1; }
uint32_t D = create_ent (root, "D");
if (!check_hierarchy_size (ref->hierarchy, 12)) { return 1; }
if (!check_indices (root, 0, nullindex, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 10, 0)) { return 1; }
if (!check_indices ( D, 4, 0, 10, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 10, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 11, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 11, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 11, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 12, 0)) { return 1; }
if (!check_indices (A1a, 10, 5, 12, 0)) { return 1; }
if (!check_indices (B2a, 11, 8, 12, 0)) { return 1; }
dump_hierarchy (ref->hierarchy);
uint32_t C1 = create_ent (C, "C1");
dump_hierarchy (ref->hierarchy);
if (!check_hierarchy_size (ref->hierarchy, 13)) { return 1; }
if (!check_indices (root, 0, nullindex, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 10, 1)) { return 1; }
if (!check_indices ( D, 4, 0, 11, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; }
if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; }
if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; }
if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; }
dump_tree (ref->hierarchy, 0, 0);
Hierarchy_SetTreeMode (ref->hierarchy, true);
//ref->hierarchy->tree_mode = true;
dump_hierarchy (ref->hierarchy);
dump_tree (ref->hierarchy, 0, 0);
if (!check_for_loops (ref->hierarchy)) { return 1; }
if (!check_next_last_indices (ref->hierarchy)) { return 1; }
create_ent (root, "E");
create_ent (B1, "B1a");
create_ent (A2, "A2a");
dump_hierarchy (ref->hierarchy);
dump_tree (ref->hierarchy, 0, 0);
if (!check_for_loops (ref->hierarchy)) { return 1; }
if (!check_next_last_indices (ref->hierarchy)) { return 1; }
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (ref->hierarchy);
return 0;
}
static int
test_build_hierarchy2 (void)
{
printf ("test_build_hierarchy2\n");
uint32_t root = create_ent (nullent, "root");
hierref_t *ref = Ent_GetComponent (root, test_href, test_reg);
Hierarchy_SetTreeMode (ref->hierarchy, true);
uint32_t A = create_ent (root, "A");
uint32_t A1 = create_ent (A, "A1");
uint32_t A1a = create_ent (A1, "A1a");
uint32_t A2 = create_ent (A, "A2");
uint32_t B = create_ent (root, "B");
uint32_t B1 = create_ent (B, "B1");
uint32_t B2 = create_ent (B, "B2");
uint32_t B2a = create_ent (B2, "B2a");
uint32_t B3 = create_ent (B, "B3");
uint32_t C = create_ent (root, "C");
uint32_t C1 = create_ent (C, "C1");
uint32_t D = create_ent (root, "D");
//check_indices (ent, index, parentIndex, childIndex, childCount)
#define ni nullindex
if (!check_indices (root, 0, ni, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 2, 2)) { return 1; }
if (!check_indices ( A1, 2, 1, 3, 1)) { return 1; }
if (!check_indices (A1a, 3, 2, ni, 0)) { return 1; }
if (!check_indices ( A2, 4, 1, ni, 0)) { return 1; }
if (!check_indices ( B, 5, 0, 6, 3)) { return 1; }
if (!check_indices ( B1, 6, 5, ni, 0)) { return 1; }
if (!check_indices ( B2, 7, 5, 8, 1)) { return 1; }
if (!check_indices (B2a, 8, 7, ni, 0)) { return 1; }
if (!check_indices ( B3, 9, 5, ni, 0)) { return 1; }
if (!check_indices ( C, 10, 0, 11, 1)) { return 1; }
if (!check_indices ( C1, 11, 10, ni, 0)) { return 1; }
if (!check_indices ( D, 12, 0, ni, 0)) { return 1; }
if (!check_next_last_indices (ref->hierarchy)) { return 1; }
if (!check_for_loops (ref->hierarchy)) { return 1; }
dump_hierarchy (ref->hierarchy);
dump_tree (ref->hierarchy, 0, 0);
Hierarchy_SetTreeMode (ref->hierarchy, true);// shouldn't do anything
if (!check_indices (root, 0, ni, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 2, 2)) { return 1; }
if (!check_indices ( A1, 2, 1, 3, 1)) { return 1; }
if (!check_indices (A1a, 3, 2, ni, 0)) { return 1; }
if (!check_indices ( A2, 4, 1, ni, 0)) { return 1; }
if (!check_indices ( B, 5, 0, 6, 3)) { return 1; }
if (!check_indices ( B1, 6, 5, ni, 0)) { return 1; }
if (!check_indices ( B2, 7, 5, 8, 1)) { return 1; }
if (!check_indices (B2a, 8, 7, ni, 0)) { return 1; }
if (!check_indices ( B3, 9, 5, ni, 0)) { return 1; }
if (!check_indices ( C, 10, 0, 11, 1)) { return 1; }
if (!check_indices ( C1, 11, 10, ni, 0)) { return 1; }
if (!check_indices ( D, 12, 0, ni, 0)) { return 1; }
if (!check_next_last_indices (ref->hierarchy)) { return 1; }
if (!check_for_loops (ref->hierarchy)) { return 1; }
Hierarchy_SetTreeMode (ref->hierarchy, false);
puts("Hierarchy_SetTreeMode");
dump_hierarchy (ref->hierarchy);
dump_tree (ref->hierarchy, 0, 0);
if (!check_indices (root, 0, nullindex, 1, 4)) { return 1; }
if (!check_indices ( A, 1, 0, 5, 2)) { return 1; }
if (!check_indices ( B, 2, 0, 7, 3)) { return 1; }
if (!check_indices ( C, 3, 0, 10, 1)) { return 1; }
if (!check_indices ( D, 4, 0, 11, 0)) { return 1; }
if (!check_indices ( A1, 5, 1, 11, 1)) { return 1; }
if (!check_indices ( A2, 6, 1, 12, 0)) { return 1; }
if (!check_indices ( B1, 7, 2, 12, 0)) { return 1; }
if (!check_indices ( B2, 8, 2, 12, 1)) { return 1; }
if (!check_indices ( B3, 9, 2, 13, 0)) { return 1; }
if (!check_indices ( C1, 10, 3, 13, 0)) { return 1; }
if (!check_indices (A1a, 11, 5, 13, 0)) { return 1; }
if (!check_indices (B2a, 12, 8, 13, 0)) { return 1; }
// Delete the hierarchy directly as setparent isn't fully tested
Hierarchy_Delete (ref->hierarchy);
return 0;
}
int
main (void)
{
test_reg = ECS_NewRegistry ();
ECS_RegisterComponents (test_reg, test_components, test_num_components);
ECS_CreateComponentPools (test_reg);
if (test_build_hierarchy ()) { return 1; }
if (test_build_hierarchy2 ()) { return 1; }
ECS_DelRegistry (test_reg);
return 0;
}

View file

@ -278,6 +278,8 @@ IMUI_BeginFrame (imui_ctx_t *ctx)
auto root_size = View_GetLen (ctx->root_view); auto root_size = View_GetLen (ctx->root_view);
Ent_RemoveComponent (root_ent, ctx->root_view.comp, ctx->root_view.reg); Ent_RemoveComponent (root_ent, ctx->root_view.comp, ctx->root_view.reg);
ctx->root_view = View_AddToEntity (root_ent, ctx->vsys, nullview); ctx->root_view = View_AddToEntity (root_ent, ctx->vsys, nullview);
auto ref = View_GetRef (ctx->root_view);
Hierarchy_SetTreeMode (ref->hierarchy, true);
View_SetLen (ctx->root_view, root_size.x, root_size.y); View_SetLen (ctx->root_view, root_size.x, root_size.y);
ctx->frame_start = Sys_LongTime (); ctx->frame_start = Sys_LongTime ();
ctx->frame_count++; ctx->frame_count++;
@ -583,6 +585,8 @@ void
IMUI_Draw (imui_ctx_t *ctx) IMUI_Draw (imui_ctx_t *ctx)
{ {
ctx->frame_draw = Sys_LongTime (); ctx->frame_draw = Sys_LongTime ();
auto ref = View_GetRef (ctx->root_view);
Hierarchy_SetTreeMode (ref->hierarchy, false);
prune_objects (ctx); prune_objects (ctx);
layout_objects (ctx); layout_objects (ctx);