polyadvent

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

resource.c (7230B)


      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 void init_id(struct resource_id *id) {
     35     id->index = -1;
     36     id->generation = -1;
     37     id->uuid = -1;
     38 }
     39 
     40 void null_id(struct resource_id *id)
     41 {
     42     id->generation = 0;
     43     id->uuid = -1;
     44     id->index = -1;
     45 }
     46 
     47 static void init_static_ids(struct resource_manager *r)
     48 {
     49     for (u32 i = 0; i < r->static_elems; i++)
     50         r->ids[i] = make_static_id(i);
     51 }
     52 
     53 void init_resource_manager(struct resource_manager *r, u32 elem_size,
     54                            u32 initial_elements, u32 max_elements,
     55                            const char *name,
     56                            int static_elems)
     57 {
     58     r->generation = 1;
     59     r->resource_count = static_elems;
     60     r->elem_size = elem_size;
     61     r->max_capacity = max_elements;
     62     r->current_capacity = initial_elements + static_elems;
     63     r->name = name;
     64     r->static_elems = static_elems;
     65 
     66     assert(initial_elements != 0);
     67 
     68     r->resources = calloc(r->current_capacity, elem_size);
     69     r->ids = calloc(r->current_capacity, sizeof(struct resource_id));
     70 
     71     init_static_ids(r);
     72 }
     73 
     74 void destroy_resource_manager(struct resource_manager *r) {
     75     free(r->ids);
     76     free(r->resources);
     77 }
     78 
     79 static int refresh_id(struct resource_manager *r, struct resource_id *id,
     80                       struct resource_id *new)
     81 {
     82     // rollover is ok
     83     /* assert(->generation <= esys.generation); */
     84     if (id->generation != r->generation) {
     85         /* debug("id %llu gen %d != res gen %d, refreshing\n", */
     86         /*       id->uuid, id->generation, r->generation); */
     87         // try to find uuid in new memory layout
     88         for (u32 i = r->static_elems; i < r->resource_count; i++) {
     89             struct resource_id *newer_id = &r->ids[i];
     90             if (newer_id->uuid == id->uuid) {
     91                 /* debug("found %llu, ind %d -> %d\n", new_id->uuid, new_id->index, new->index); */
     92                 new->index = newer_id->index;
     93                 new->generation = r->generation;
     94                 return REFRESHED_ID;
     95             }
     96         }
     97 
     98         // entity was deleted
     99         return RESOURCE_DELETED;
    100     }
    101 
    102     // doesn't need refreshed
    103     return REFRESH_NOT_NEEDED;
    104 }
    105  
    106 int is_resource_destroyed(struct resource_id *id) {
    107     return id->generation == 0;
    108 }
    109 
    110 static void new_id(struct resource_manager *r, struct resource_id *id)
    111 {
    112     id->index = r->resource_count;
    113     id->uuid  = ++resource_uuids;
    114     id->generation = r->generation;
    115     assert(id->generation);
    116 }
    117 
    118 static void resize(struct resource_manager *r)
    119 {
    120     debug("resizing %s resources, count %d+1 > current capacity %d\n",
    121           r->name, r->resource_count, r->current_capacity);
    122     void *new_mem;
    123     u32 new_size = r->resource_count * 1.5;
    124     if (new_size >= r->max_capacity)
    125         new_size = r->max_capacity;
    126 
    127     /* debug("resizing new_size %d\n", new_size); */
    128 
    129     new_mem = realloc(r->resources, (new_size+1) * r->elem_size);
    130     if (!new_mem) {
    131         // yikes, out of memory, bail
    132         assert(new_mem);
    133         return;
    134     }
    135 
    136     r->resources = new_mem;
    137     new_mem = realloc(r->ids, sizeof(struct resource_id) * (new_size+1));
    138 
    139     if (!new_mem) {
    140         // yikes, out of memory, bail
    141         assert(new_mem);
    142         return;
    143     }
    144     r->current_capacity = new_size;
    145     r->ids = new_mem;
    146 }
    147 
    148 void print_id(struct resource_id *id, int nl)
    149 {
    150     int is_static = is_static_resource(id);
    151 
    152     if (is_static) {
    153         printf("sid(%d)%s", id->index, nl?"\n":"");
    154         return;
    155     }
    156 
    157     printf("id(u:%llu i:%d g:%d)%s",
    158            id->uuid, id->index, id->generation, nl?"\n":"");
    159 }
    160 
    161 
    162 void *new_resource(struct resource_manager *r, struct resource_id *id)
    163 {
    164     assert(id);
    165     assert(id->uuid != STATIC_UUID && "called new_resource with a static id");
    166     assert(id->index == 0xFFFFFFFF && "res_id is uninitialized");
    167     assert(id->index >= r->static_elems);
    168 
    169     struct resource_id *fresh_id;
    170 
    171     if (r->resource_count + 1 > r->max_capacity) {
    172         printf("new_resource: count %d > max cap %d\n", r->resource_count, r->max_capacity);
    173         return NULL;
    174     }
    175 
    176     if (r->resource_count + 1 > r->current_capacity)
    177         resize(r);
    178 
    179     fresh_id = &r->ids[r->resource_count];
    180     new_id(r, fresh_id);
    181     *id = *fresh_id;
    182 
    183     return index_resource(r, r->resource_count++);
    184 }
    185 
    186 
    187 void *get_static_resource(struct resource_manager *r,
    188                           struct resource_id *id)
    189 {
    190     assert(id->index < r->static_elems && "oob static index");
    191 
    192     return index_resource(r, id->index);
    193 }
    194 
    195 void *get_resource(struct resource_manager *r, struct resource_id *id) {
    196     if (id->uuid == STATIC_UUID)
    197         return get_static_resource(r, id);
    198 
    199     assert((int64_t)id->generation != -1 && "id intialized but not allocated (needs new_ call)");
    200 
    201     if (id->generation == 0) {
    202         /* unusual("getting already deleted resource %llu\n", id->uuid); */
    203         return NULL;
    204     }
    205 
    206     enum refresh_status res = refresh_id(r, id, id);
    207 
    208     if (res == RESOURCE_DELETED) {
    209         /* unusual("getting deleted %s resource %llu\n", r->name, id->uuid); */
    210         return NULL;
    211     }
    212 
    213     return index_resource(r, id->index);
    214 }
    215 
    216 
    217 void destroy_resource(struct resource_manager *r, struct resource_id *id) {
    218     assert(id->index >= r->static_elems && "cant destroy a static resource");
    219 
    220     if (id->index < r->static_elems) {
    221         unusual("trying to destroy a static %s resource #%d\n", r->name, id->index);
    222         return;
    223     }
    224 
    225     if (is_resource_destroyed(id)) {
    226         unusual("trying to destroy resource %llu which was already destroyed\n", id->uuid);
    227         return;
    228     }
    229 
    230     enum refresh_status res = refresh_id(r, id, id);
    231 
    232     // entity already deleted
    233     /* debug("refresh res %d uuid %llu gen %d index %d\n", res, */
    234     /*       id->uuid, id->generation, id->index); */
    235 
    236     if (res == RESOURCE_DELETED) {
    237         unusual("trying to destroy resource %llu which was already destroyed (2)\n", id->uuid);
    238         id->generation = 0;
    239         return;
    240     }
    241 
    242     /* debug("destroying %s resource %llu ind %d res_count %d\n", */
    243     /*       r->name, id->uuid, id->index, r->resource_count); */
    244 
    245     r->resource_count--;
    246     assert(r->resource_count >= r->static_elems);
    247     r->generation++;
    248 
    249     assert((int)r->resource_count - (int)id->index >= 0);
    250 
    251     // TODO: we're copying OOB here
    252     memmove(index_resource(r, id->index),
    253             index_resource(r, id->index+1),
    254             r->elem_size * (r->resource_count - id->index));
    255 
    256     memmove(&r->ids[id->index],
    257             &r->ids[id->index+1],
    258             sizeof(struct resource_id) * (r->resource_count - id->index));
    259 
    260 
    261     for (u32 i = id->index; i < r->resource_count; i++) {
    262         r->ids[i].index--;
    263     }
    264 }