polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit 75faf6813342d666d3eb289e16a421f34d44b025
parent 250c31bfacae9a899611ad2c6e560e081136bc98
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 28 Jun 2019 03:09:26 -0700

resource allocator progress

Diffstat:
M.gitignore | 1+
MMakefile | 9++++++---
Msrc/common.h | 4+++-
Msrc/debug.h | 3+++
Msrc/entity.c | 5+++++
Msrc/entity.h | 3++-
Msrc/game.c | 1+
Msrc/model.c | 5+++++
Msrc/model.h | 4+++-
Msrc/resource.c | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/resource.h | 6++++--
Atest/test_resource.c | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +}