polyadvent

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

resource.c (5415B)


      1 
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <assert.h>
      5 
      6 #include "resource.h"
      7 #include "debug.h"
      8 #include "util.h"
      9 
     10 static u64 resource_uuids = 0;
     11 
     12 static inline void *index_resource_(u8 *res, u32 elem_size, int i)
     13 {
     14 	assert(res);
     15 	return res + (i * elem_size);
     16 }
     17 
     18 static inline void *index_resource(struct resource_manager *r, int i)
     19 {
     20 	return index_resource_(r->resources, r->elem_size, i);
     21 }
     22 
     23 
     24 void *get_all_resources(struct resource_manager *r, u32 *count, struct resource_id **ids) {
     25 	if (count != 0)
     26 		*count = r->resource_count;
     27 
     28 	if (ids != 0)
     29 		*ids = r->ids;
     30 
     31 	return r->resources;
     32 }
     33 
     34 
     35 void init_resource_manager(struct resource_manager *r, u32 elem_size,
     36 						   u32 initial_elements, u32 max_elements,
     37 						   const char *name)
     38 {
     39 	r->generation = 1;
     40 	r->resource_count = 0;
     41 	r->elem_size = elem_size;
     42 	r->max_capacity = max_elements;
     43 	r->current_capacity = initial_elements;
     44 	r->name = name;
     45 
     46 	assert(initial_elements != 0);
     47 
     48 	r->resources = calloc(r->current_capacity, elem_size);
     49 	r->ids = calloc(r->current_capacity, sizeof(struct resource_id));
     50 }
     51 
     52 void destroy_resource_manager(struct resource_manager *r) {
     53 	free(r->ids);
     54 	free(r->resources);
     55 }
     56 
     57 static int refresh_id(struct resource_manager *r, struct resource_id *id,
     58 					  struct resource_id *new)
     59 {
     60 	// rollover is ok
     61 	/* assert(->generation <= esys.generation); */
     62 	if (id->generation != r->generation) {
     63 		/* debug("id %llu gen %d != res gen %d, refreshing\n", */
     64 		/*		 id->uuid, id->generation, r->generation); */
     65 		// try to find uuid in new memory layout
     66 		for (u32 i = 0; i < r->resource_count; i++) {
     67 			struct resource_id *newer_id = &r->ids[i];
     68 			if (newer_id->uuid == id->uuid) {
     69 				/* debug("found %llu, ind %d -> %d\n", new_id->uuid, new_id->index, new->index); */
     70 				new->index = newer_id->index;
     71 				new->generation = r->generation;
     72 				return REFRESHED_ID;
     73 			}
     74 		}
     75 
     76 		// entity was deleted
     77 		return RESOURCE_DELETED;
     78 	}
     79 
     80 	// doesn't need refreshed
     81 	return REFRESH_NOT_NEEDED;
     82 }
     83  
     84 int is_resource_destroyed(struct resource_manager *r, struct resource_id *id) {
     85 	return refresh_id(r, id, id) == RESOURCE_DELETED;
     86 }
     87 
     88 static void new_id(struct resource_manager *r, struct resource_id *id)
     89 {
     90 	id->index = r->resource_count;
     91 	id->uuid  = ++resource_uuids;
     92 	id->generation = r->generation;
     93 	assert(id->generation);
     94 }
     95 
     96 static void resize(struct resource_manager *r)
     97 {
     98 	debug("resizing %s resources, count %d+1 > current capacity %d\n",
     99 		  r->name, r->resource_count, r->current_capacity);
    100 	void *new_mem;
    101 	u32 new_size = r->resource_count * 1.5;
    102 	if (new_size >= r->max_capacity)
    103 		new_size = r->max_capacity;
    104 
    105 	/* debug("resizing new_size %d\n", new_size); */
    106 
    107 	new_mem = realloc(r->resources, (new_size+1) * r->elem_size);
    108 	if (!new_mem) {
    109 		// yikes, out of memory, bail
    110 		assert(new_mem);
    111 		return;
    112 	}
    113 
    114 	r->resources = new_mem;
    115 	new_mem = realloc(r->ids, sizeof(struct resource_id) * (new_size+1));
    116 
    117 	if (!new_mem) {
    118 		// yikes, out of memory, bail
    119 		assert(new_mem);
    120 		return;
    121 	}
    122 	r->current_capacity = new_size;
    123 	r->ids = new_mem;
    124 }
    125 
    126 void print_id(struct resource_id *id, int nl)
    127 {
    128 	printf("id(u:%" PRIu64 " i:%d g:%d)%s",
    129 		   id->uuid, id->index, id->generation, nl?"\n":"");
    130 }
    131 
    132 
    133 void *new_resource(struct resource_manager *r, struct resource_id *id)
    134 {
    135 	assert(id);
    136 	assert(id->index == 0xFFFFFFFF && "res_id is uninitialized");
    137 
    138 	struct resource_id *fresh_id;
    139 
    140 	if (r->resource_count + 1 > r->max_capacity) {
    141 		printf("new_resource: count %d > max cap %d\n", r->resource_count, r->max_capacity);
    142 		return NULL;
    143 	}
    144 
    145 	if (r->resource_count + 1 > r->current_capacity)
    146 		resize(r);
    147 
    148 	fresh_id = &r->ids[r->resource_count];
    149 	new_id(r, fresh_id);
    150 	*id = *fresh_id;
    151 
    152 	return index_resource(r, r->resource_count++);
    153 }
    154 
    155 
    156 void *get_resource(struct resource_manager *r, struct resource_id *id) {
    157 	assert((int64_t)id->generation != -1 && "id intialized but not allocated (needs new_ call)");
    158 
    159 	if (id->generation == 0) {
    160 		/* unusual("getting already deleted resource %llu\n", id->uuid); */
    161 		return NULL;
    162 	}
    163 
    164 	enum refresh_status res = refresh_id(r, id, id);
    165 
    166 	if (res == RESOURCE_DELETED) {
    167 		/* unusual("getting deleted %s resource %llu\n", r->name, id->uuid); */
    168 		return NULL;
    169 	}
    170 
    171 	return index_resource(r, id->index);
    172 }
    173 
    174 
    175 void destroy_resource(struct resource_manager *r, struct resource_id *id) {
    176 	if (is_resource_destroyed(r, id)) {
    177 		unusual("trying to destroy resource %" PRIu64 " which was already destroyed\n", id->uuid);
    178 		return;
    179 	}
    180 
    181 	enum refresh_status res = refresh_id(r, id, id);
    182 
    183 	// entity already deleted
    184 	/* debug("refresh res %d uuid %llu gen %d index %d\n", res, */
    185 	/*		 id->uuid, id->generation, id->index); */
    186 
    187 	if (res == RESOURCE_DELETED) {
    188 		unusual("trying to destroy resource %" PRIu64 " which was already destroyed (2)\n", id->uuid);
    189 		id->generation = 0;
    190 		return;
    191 	}
    192 
    193 	/* debug("destroying %s resource %llu ind %d res_count %d\n", */
    194 	/*		 r->name, id->uuid, id->index, r->resource_count); */
    195 
    196 	r->resource_count--;
    197 	r->generation++;
    198 
    199 	if (r->resource_count > 0) {
    200 		assert((int)r->resource_count - (int)id->index >= 0);
    201 
    202 		// TODO: we're copying OOB here
    203 		memmove(index_resource(r, id->index),
    204 			index_resource(r, id->index+1),
    205 			r->elem_size * (r->resource_count - id->index));
    206 
    207 		memmove(&r->ids[id->index],
    208 			&r->ids[id->index+1],
    209 			sizeof(struct resource_id) * (r->resource_count - id->index));
    210 	}
    211 
    212 	for (u32 i = id->index; i < r->resource_count; i++) {
    213 		r->ids[i].index--;
    214 	}
    215 }