commit 75faf6813342d666d3eb289e16a421f34d44b025
parent 250c31bfacae9a899611ad2c6e560e081136bc98
Author: William Casarin <jb55@jb55.com>
Date: Fri, 28 Jun 2019 03:09:26 -0700
resource allocator progress
Diffstat:
12 files changed, 277 insertions(+), 30 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -4,3 +4,4 @@ polyadvent
/TAGS
*.d
/test/test_animation
+/test/test_resource
diff --git a/Makefile b/Makefile
@@ -6,6 +6,7 @@ CFLAGS = $(DEFS) -ggdb -O2 -I src -Wall -Werror -Wextra -std=c99 \
-Wno-unused-function \
-Wno-unused-parameter \
-Wno-unused-variable \
+ -Wmissing-field-initializers \
-Wno-cast-align \
-Wno-padded
LDFLAGS = -lSDL2 -lGL -lm
@@ -49,14 +50,15 @@ OBJS += $(SRC)/scene.o
OBJS += $(SRC)/resource.o
OBJS += $(SRC)/quickhull.o
-TESTS = test/test_animation
+TESTS = test/test_animation
+TESTS += test/test_resource
SRCS=$(OBJS:.o=.c)
all: $(BIN)
clean:
- rm -f src/main.o $(OBJS) $(TESTS) $(SHLIB) $(BIN) $(SRC)/*.d*
+ rm -f src/main.o test/*.o $(OBJS) $(TESTS) $(SHLIB) $(BIN) $(SRC)/*.d*
include $(OBJS:.o=.d)
include src/main.d
@@ -72,7 +74,8 @@ test/%: test/%.o $(OBJS)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
check: $(TESTS)
- ./$(TESTS)
+ ./test/test_animation
+ ./test/test_resource
$(BIN): src/main.o $(OBJS)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
diff --git a/src/common.h b/src/common.h
@@ -2,7 +2,9 @@
#ifndef POLYADVENT_COMMON_H
#define POLYADVENT_COMMON_H
-#include <stdbool.h>
+typedef int bool;
+#define false 0
+#define true 1
#define MAT3_ELEMS 9
#define MAT4_ELEMS 16
diff --git a/src/debug.h b/src/debug.h
@@ -6,5 +6,8 @@
void show_info_log(GLuint shader);
+#define unusual(...) fprintf(stderr, "UNUSUAL: " __VA_ARGS__)
+#define debug(...) fprintf(stderr, "debug: " __VA_ARGS__)
+
#endif /* POLYADVENT_DEBUG_H */
diff --git a/src/entity.c b/src/entity.c
@@ -9,6 +9,11 @@
static struct resource_manager esys;
+struct resource_manager *_internal_get_entity_system()
+{
+ return &esys;
+}
+
struct entity *get_all_entities(u32 *count, entity_id **ids) {
return get_all_resources(&esys, count, ids);
}
diff --git a/src/entity.h b/src/entity.h
@@ -27,11 +27,12 @@ typedef struct resource_id entity_id;
struct entity *init_entity(struct entity *);
void destroy_entities();
+struct resource_manager *_internal_get_entity_system();
void destroy_entity(entity_id *);
void init_entity_system();
struct entity *get_entity(entity_id *);
struct entity *get_all_entities(u32 *count, entity_id **ids);
struct entity *new_entity(entity_id *id);
-
+void destroy_entity_system();
#endif /* ENTITY_H */
diff --git a/src/game.c b/src/game.c
@@ -103,6 +103,7 @@ void proc_sphere(struct make_geometry *mkgeom, geometry_id *geom_id) {
void game_init(struct game *game, int width, int height) {
init_gl(&game->test_resources, width, height);
init_entity_system();
+ init_model_manager();
init_geometry_manager();
init_user_settings(&game->user_settings);
check_gl();
diff --git a/src/model.c b/src/model.c
@@ -35,6 +35,11 @@ static inline struct model *new_uninitialized_model(model_id *id) {
return new_resource(&dyn_modelman, id);
}
+void init_model_manager() {
+ init_resource_manager(&dyn_modelman, sizeof(struct model),
+ DEF_DYNAMIC_MODELS, MAX_DYNAMIC_MODELS);
+}
+
struct model *new_dynamic_model(model_id *id)
{
diff --git a/src/model.h b/src/model.h
@@ -8,8 +8,9 @@
#include "geometry.h"
#include "common.h"
+#define DEF_DYNAMIC_MODELS 128
#define MAX_STATIC_MODELS 128
-#define MAX_DYNAMIC_MODELS 512
+#define MAX_DYNAMIC_MODELS 2048
typedef struct resource_id model_id;
@@ -40,6 +41,7 @@ struct model_def {
};
+void init_model_manager();
struct model *init_model(struct model *model);
struct model *get_model(enum static_model);
diff --git a/src/resource.c b/src/resource.c
@@ -1,9 +1,12 @@
-#include "resource.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include "resource.h"
+#include "debug.h"
+#include "util.h"
+
static u64 resource_uuids = 0;
static inline void *index_resource(struct resource_manager *r, int i) {
@@ -13,8 +16,10 @@ static inline void *index_resource(struct resource_manager *r, int i) {
void *get_all_resources(struct resource_manager *r, u32 *count, struct resource_id **ids) {
if (count != 0)
*count = r->resource_count;
+
if (ids != 0)
*ids = r->ids;
+
return r->resources;
}
@@ -29,7 +34,10 @@ void init_resource_manager(struct resource_manager *r, u32 elem_size,
r->generation = 1;
r->resource_count = 0;
r->elem_size = elem_size;
- r->max_elements = max_elements;
+ r->max_capacity = max_elements;
+ r->current_capacity = initial_elements;
+
+ assert(initial_elements != 0);
r->resources = calloc(initial_elements, elem_size);
r->ids = calloc(initial_elements, sizeof(*r->ids));
@@ -46,6 +54,7 @@ static int refresh_id(struct resource_manager *r, struct resource_id *id,
// rollover is ok
/* assert(->generation <= esys.generation); */
if (id->generation != r->generation) {
+ debug("id gen %d != res gen %d, refreshing\n", id->generation, r->generation);
// try to find uuid in new memory layout
for (u32 i = 0; i < r->resource_count; i++) {
struct resource_id *new_id = &r->ids[i];
@@ -68,37 +77,131 @@ int is_resource_destroyed(struct resource_id *id) {
return id->generation == 0;
}
-void destroy_resource(struct resource_manager *r, struct resource_id *id) {
- if (is_resource_destroyed(id))
+/* static struct resource_id new_id(struct resource_manager *r) { */
+/* return (struct resource_id){ */
+/* .index = r->resource_count, */
+/* .uuid = ++resource_uuids, */
+/* .generation = r->generation, */
+/* }; */
+/* } */
+
+static void new_id(struct resource_manager *r, struct resource_id *id)
+{
+ id->index = r->resource_count;
+ id->uuid = ++resource_uuids;
+ id->generation = r->generation;
+ assert(id->generation);
+}
+
+static void resize(struct resource_manager *r)
+{
+ void *new_mem;
+ u32 new_size = r->resource_count * 1.5;
+ if (new_size >= r->max_capacity)
+ new_size = r->max_capacity;
+ debug("resizing new_size %d\n", new_size);
+ new_mem = realloc(r->resources, new_size * r->elem_size);
+ if (!new_mem) {
+ // yikes, out of memory, bail
+ assert(new_mem);
return;
+ }
- int res = refresh_id(r, id, id);
- // entity already deleted
- if (res == 0) {
- id->generation = 0;
+ r->resources = new_mem;
+ new_mem = realloc(r->ids, sizeof(struct resource_id) * new_size);
+
+ if (!new_mem) {
+ // yikes, out of memory, bail
+ assert(new_mem);
return;
}
- // generation 0 means destroyed
- id->generation = 0;
+ r->current_capacity = new_size;
+ r->ids = new_mem;
- assert(!"implement destroy resource");
- /* memmove(index_resource(r, id->index), */
- /* index_resource(r, id->index+1), r->resource_count -) */
+ // 2 to avoid issues with compact generation incrementing
+ r->generation += 2;
+}
+
+static int compact(struct resource_manager *r)
+{
+ int free_slot = -1;
+ int fresh_elements = 0;
+ u8 *dst;
+ u8 *src = NULL;
+ u8 * const start = r->resources;
+
+ for (int i = 0; i < (int)r->resource_count; i++) {
+ if (free_slot != -1 && r->ids[i].generation != 0) {
+ debug("res: moving from %d to empty slot %d\n", i, free_slot);
+ src = index_resource(r, i);
+ dst = index_resource(r, free_slot);
+
+ if (src == dst) {
+ unusual("resource compact: trying to compact into the same location\n");
+ continue;
+ }
+
+ debug("res: src %zu dst %zu count %d cap %d\n", src - start, dst - start,
+ r->resource_count, r->current_capacity);
+
+ // move data to free slot, clear the old slot
+ assert(dst + r->elem_size <= start + r->elem_size * r->current_capacity);
+ memcpy(dst, src, r->elem_size);
+ assert(src + r->elem_size <= start + r->elem_size * r->current_capacity);
+ memset(src, 0, r->elem_size);
+
+ // move ids to free slot, increasing generation
+ r->ids[free_slot] = r->ids[i];
+ r->ids[free_slot].generation++;
+ r->ids[free_slot].index = free_slot;
+
+ // clear the old data
+ memset(&r->ids[i], 0, sizeof(*r->ids));
+ free_slot = min(free_slot, i);
+ fresh_elements++;
+ }
+ else if (r->ids[i].generation == 0) {
+ debug("res: found deleted slot %d\n", i);
+ free_slot = free_slot == -1 ? i : min(free_slot, i);
+ }
+ else {
+ fresh_elements++;
+ }
+
+ }
+
+ if ((int)r->resource_count != fresh_elements)
+ debug("res: updated resource_count to %d\n", fresh_elements);
+ r->resource_count = fresh_elements;
+
+ return fresh_elements;
}
-struct resource_id new_id(struct resource_manager *r) {
- return (struct resource_id){
- .index = r->resource_count,
- .uuid = resource_uuids++,
- .generation = r->generation,
- };
+static void compact_or_resize(struct resource_manager *r)
+{
+ int orig = r->resource_count;
+ int compacted = compact(r);
+ debug("%d compacted to %d > %d?\n", orig, compacted, r->current_capacity);
+ if (compacted > (int)r->current_capacity)
+ resize(r);
}
-void *new_resource(struct resource_manager *r, struct resource_id *id) {
- struct resource_id fresh_id = new_id(r);
+void *new_resource(struct resource_manager *r, struct resource_id *id)
+{
+ struct resource_id *fresh_id;
+
+ if (r->resource_count + 1 > r->max_capacity)
+ return NULL;
+
+ if (r->resource_count + 1 >= r->current_capacity)
+ compact_or_resize(r);
+
+ fresh_id = &r->ids[r->resource_count];
+
+ new_id(r, fresh_id);
if (id)
- *id = fresh_id;
+ *id = *fresh_id;
return index_resource(r, r->resource_count++);
}
@@ -108,3 +211,29 @@ void *get_resource(struct resource_manager *r, struct resource_id *id) {
return index_resource(r, id->index);
}
+
+
+void destroy_resource(struct resource_manager *r, struct resource_id *id) {
+ if (is_resource_destroyed(id))
+ return;
+
+ int res = refresh_id(r, id, id);
+ // entity already deleted
+ if (res == 0) {
+ id->generation = 0;
+ return;
+ }
+
+ // generation 0 means destroyed
+ r->ids[id->index].generation = 0;
+ r->ids[id->index].index = -1;
+ r->ids[id->index].uuid = -1;
+
+ // clear the user's id as well, since it's no longer valid
+ id->generation = 0;
+ id->uuid = -1;
+ id->index = -1;
+
+ /* memmove(index_resource(r, id->index), */
+ /* index_resource(r, id->index+1), r->resource_count -) */
+}
diff --git a/src/resource.h b/src/resource.h
@@ -16,15 +16,17 @@ struct resource_manager {
u32 resource_count;
u32 generation;
u32 elem_size;
- u32 max_elements;
+ u32 max_capacity;
+ u32 current_capacity;
};
+#define ideq(a, b) ((a)->uuid == (b)->uuid)
+
void init_resource_id(struct resource_id *id);
void *get_resource(struct resource_manager *r, struct resource_id *id);
void *get_all_resources(struct resource_manager *, u32 *count, struct resource_id **ids);
void destroy_resource(struct resource_manager *, struct resource_id *id);
void destroy_resource_manager(struct resource_manager *);
-struct resource_id new_id(struct resource_manager *);
void *new_resource(struct resource_manager *, struct resource_id *id);
void init_resource_manager(struct resource_manager *r, u32 elem_size,
diff --git a/test/test_resource.c b/test/test_resource.c
@@ -0,0 +1,93 @@
+
+#include "resource.h"
+#include "entity.h"
+#include "model.h"
+#include "debug.h"
+#include <assert.h>
+
+void test_compact()
+{
+ struct resource_manager r;
+ struct resource_id id, first_id;
+ int *p;
+
+ init_resource_manager(&r, sizeof(int), 2, 4);
+ p = new_resource(&r, &first_id);
+ *p = 11;
+ p = new_resource(&r, &id);
+ *p = 22;
+ assert(r.current_capacity == 2);
+ destroy_resource(&r, &first_id);
+ assert(r.resource_count == 2);
+ new_resource(&r, &id);
+ assert(r.resource_count == 2);
+ assert(r.current_capacity == 2);
+ new_resource(&r, &id);
+ assert(r.current_capacity == 4);
+}
+
+void test_int_resource_manager()
+{
+ struct resource_manager r;
+ struct resource_id id, first_id;
+ int *p;
+ // 2 item case
+ init_resource_manager(&r, sizeof(int), 1, 2);
+
+ p = new_resource(&r, &first_id);
+ assert(p);
+ *p = 42;
+
+ assert(r.resource_count == 1);
+ assert(first_id.generation == 1);
+ assert(*(int*)get_resource(&r, &first_id) == 42);
+
+ p = new_resource(&r, &id);
+ assert(p);
+ *p = 32;
+ assert(r.resource_count == 2);
+ /* assert(id.generation == 2); */
+ assert(*(int*)get_resource(&r, &id) == 32);
+ assert(*(int*)get_resource(&r, &first_id) == 42);
+
+ destroy_resource_manager(&r);
+}
+
+void test_entity_system()
+{
+ u32 count;
+ struct entity *ent, *ents;
+ entity_id ent_id;
+ entity_id *ids;
+
+ init_entity_system();
+
+ ents = get_all_entities(&count, &ids);
+ assert(count == 0);
+
+ ent = new_entity(&ent_id);
+ ents = get_all_entities(&count, &ids);
+
+ assert(ent != NULL);
+ assert(count == 1);
+ assert(&ents[0] == ent);
+
+ assert(ideq(&ids[0], &ent_id));
+
+ destroy_entity_system();
+}
+
+void test_dynamic_model_manager()
+{
+ init_model_manager();
+}
+
+int main(int argc, char *argv[])
+{
+ /* test_int_resource_manager(); */
+ test_compact();
+ /* test_dynamic_model_manager(); */
+ test_entity_system();
+
+ return 0;
+}