WIP async loading

This commit is contained in:
Andrei "Akari" Alexeyev 2017-03-13 07:44:39 +02:00
parent 15c2472336
commit df90733c8f
23 changed files with 405 additions and 89 deletions

View file

@ -33,6 +33,10 @@ void handle_events(EventHandler handler, EventFlags flags, void *arg) {
}
while(SDL_PollEvent(&event)) {
if(resource_sdl_event(&event)) {
continue;
}
SDL_Scancode scan = event.key.keysym.scancode;
SDL_Keymod mod = event.key.keysym.mod;
bool repeat = event.key.repeat;

View file

@ -8,11 +8,13 @@
#include "hashtable.h"
#include "list.h"
#include "log.h"
#include <assert.h>
#include <string.h>
#include <zlib.h>
#include <stdio.h>
#include <SDL_mutex.h>
typedef struct HashtableElement {
void *next;
@ -30,6 +32,7 @@ struct Hashtable {
HTCopyFunc copy_func;
HTFreeFunc free_func;
ListContainer *deferred_unsets;
SDL_mutex *mutex;
};
typedef struct HashtableIterator {
@ -53,12 +56,23 @@ Hashtable* hashtable_new(size_t size, HTCmpFunc cmp_func, HTHashFunc hash_func,
ht->copy_func = copy_func;
ht->free_func = free_func;
ht->deferred_unsets = NULL;
ht->mutex = SDL_CreateMutex();
assert(ht->hash_func != NULL);
return ht;
}
void hashtable_lock(Hashtable *ht) {
assert(ht != NULL);
SDL_LockMutex(ht->mutex);
}
void hashtable_unlock(Hashtable *ht) {
assert(ht != NULL);
SDL_UnlockMutex(ht->mutex);
}
static void hashtable_delete_callback(void **vlist, void *velem, void *vht) {
Hashtable *ht = vht;
HashtableElement *elem = velem;
@ -84,6 +98,8 @@ void hashtable_free(Hashtable *ht) {
}
hashtable_unset_all(ht);
SDL_DestroyMutex(ht->mutex);
free(ht->table);
free(ht);
}
@ -100,14 +116,17 @@ void* hashtable_get(Hashtable *ht, void *key) {
assert(ht != NULL);
hash_t hash = ht->hash_func(key);
hashtable_lock(ht);
HashtableElement *elems = ht->table[hash % ht->table_size];
for(HashtableElement *e = elems; e; e = e->next) {
if(hash == e->hash && hashtable_compare(ht, key, e->key)) {
hashtable_unlock(ht);
return e->data;
}
}
hashtable_unlock(ht);
return NULL;
}
@ -116,6 +135,9 @@ void hashtable_set(Hashtable *ht, void *key, void *data) {
hash_t hash = ht->hash_func(key);
size_t idx = hash % ht->table_size;
hashtable_lock(ht);
HashtableElement *elems = ht->table[idx], *elem;
for(HashtableElement *e = elems; e; e = e->next) {
@ -142,6 +164,8 @@ void hashtable_set(Hashtable *ht, void *key, void *data) {
}
ht->table[idx] = elems;
hashtable_unlock(ht);
}
void hashtable_unset(Hashtable *ht, void *key) {
@ -151,6 +175,8 @@ void hashtable_unset(Hashtable *ht, void *key) {
void hashtable_unset_deferred(Hashtable *ht, void *key) {
assert(ht != NULL);
hashtable_lock(ht);
ListContainer *c = create_element((void**)&ht->deferred_unsets, sizeof(ListContainer));
if(ht->copy_func) {
@ -158,17 +184,23 @@ void hashtable_unset_deferred(Hashtable *ht, void *key) {
} else {
c->data = key;
}
hashtable_unlock(ht);
}
void hashtable_unset_deferred_now(Hashtable *ht) {
ListContainer *next;
assert(ht != NULL);
hashtable_lock(ht);
for(ListContainer *c = ht->deferred_unsets; c; c = next) {
next = c->next;
hashtable_unset(ht, c->data);
delete_element((void**)&ht->deferred_unsets, c);
}
hashtable_unlock(ht);
}
/*
@ -180,6 +212,8 @@ void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg) {
void *ret = NULL;
hashtable_lock(ht);
for(size_t i = 0; i < ht->table_size; ++i) {
for(HashtableElement *e = ht->table[i]; e; e = e->next) {
ret = callback(e->key, e->data, arg);
@ -189,6 +223,8 @@ void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg) {
}
}
hashtable_unlock(ht);
return ret;
}

View file

@ -33,6 +33,8 @@ void hashtable_unset_deferred_now(Hashtable *ht);
void hashtable_unset_all(Hashtable *ht);
void* hashtable_foreach(Hashtable *ht, HTIterCallback callback, void *arg);
// THIS IS NOT THREAD-SAFE. You have to use hashtable_lock/unlock to make it so.
HashtableIterator* hashtable_iter(Hashtable *ht);
bool hashtable_iter_next(HashtableIterator *iter, void **out_key, void **out_data);
@ -54,4 +56,7 @@ int hashtable_test(void);
void hashtable_print_stringkeys(Hashtable *ht);
size_t hashtable_get_approx_overhead(Hashtable *ht);
void hashtable_lock(Hashtable *ht);
void hashtable_unlock(Hashtable *ht);
#endif

View file

@ -63,10 +63,11 @@ void draw_char_menu(MenuData *menu) {
int i;
for(i = 0; i < menu->ecount; i++) {
strlcpy(buf, menu->entries[i].name, sizeof(buf));
char *save;
char *tex = strtok(buf,"|");
char *name = strtok(NULL, "|");
char *title = strtok(NULL, "|");
char *tex = strtok_r(buf,"|", &save);
char *name = strtok_r(NULL, "|", &save);
char *title = strtok_r(NULL, "|", &save);
if(!(tex && name && title))
continue;
@ -95,8 +96,8 @@ void draw_char_menu(MenuData *menu) {
strlcpy(buf, mod->entries[i].name, sizeof(buf));
char *mari = strtok(buf, "|");
char *youmu = strtok(NULL, "|");
char *mari = strtok_r(buf, "|", &save);
char *youmu = strtok_r(NULL, "|", &save);
char *use = menu->entries[menu->cursor].arg == (void *)Marisa ? mari : youmu;

View file

@ -50,6 +50,10 @@ troll2:
if(info) {
stage_loop(info);
} else {
for(StageInfo *s = stages; s->type == STAGE_STORY; ++s) {
s->procs->preload();
}
for(StageInfo *s = stages; s->type == STAGE_STORY; ++s) {
stage_loop(s);
}

View file

@ -19,7 +19,12 @@ bool check_animation_path(const char *path) {
return strendswith(path, ANI_EXTENSION);
}
void* load_animation(const char *filename, unsigned int flags) {
typedef struct AnimationLoadData {
Animation *ani;
char *basename;
} AnimationLoadData;
void* load_animation_begin(const char *filename, unsigned int flags) {
char *basename = resource_util_basename(ANI_PATH_PREFIX, filename);
char name[strlen(basename) + 1];
strcpy(name, basename);
@ -32,8 +37,6 @@ void* load_animation(const char *filename, unsigned int flags) {
return NULL;
}
#define ANIFAIL(what) { log_warn("Bad '" what "' in animation '%s'", basename); free(ani); free(basename); return NULL; }
Animation *ani = malloc(sizeof(Animation));
ani->rows = atoi((char*)hashtable_get_string(ht, "rows"));
ani->cols = atoi((char*)hashtable_get_string(ht, "cols"));
@ -41,8 +44,10 @@ void* load_animation(const char *filename, unsigned int flags) {
hashtable_foreach(ht, hashtable_iter_free_data, NULL);
hashtable_free(ht);
#define ANIFAIL(what) { log_warn("Bad '" what "' in animation '%s'", basename); free(ani); free(basename); return NULL; }
if(ani->rows < 1) ANIFAIL("rows")
if(ani->cols < 1) ANIFAIL("cols")
#undef ANIFAIL
if(ani->speed == 0) {
log_warn("Animation speed of %s == 0. relativity?", name);
@ -51,21 +56,36 @@ void* load_animation(const char *filename, unsigned int flags) {
return NULL;
}
ani->tex = get_resource(RES_TEXTURE, basename, flags)->texture;
AnimationLoadData *data = malloc(sizeof(AnimationLoadData));
data->ani = ani;
data->basename = basename;
return data;
}
void* load_animation_end(void *opaque, const char *filename, unsigned int flags) {
AnimationLoadData *data = opaque;
if(!data) {
return NULL;
}
Animation *ani = data->ani;
ani->tex = get_resource(RES_TEXTURE, data->basename, flags)->texture;
if(!ani->tex) {
log_warn("Couldn't get texture '%s'", basename);
free(basename);
log_warn("Couldn't get texture '%s'", data->basename);
free(data->basename);
free(data);
return NULL;
}
ani->w = ani->tex->w / ani->cols;
ani->h = ani->tex->h / ani->rows;
free(basename);
return (void*)ani;
free(data->basename);
free(data);
#undef ANIFAIL
return ani;
}
Animation *get_ani(const char *name) {

View file

@ -26,7 +26,8 @@ typedef struct Animation {
char* animation_path(const char *name);
bool check_animation_path(const char *path);
void* load_animation(const char *filename, unsigned int flags);
void* load_animation_begin(const char *filename, unsigned int flags);
void* load_animation_end(void *opaque, const char *filename, unsigned int flags);
Animation *get_ani(const char *name);

View file

@ -17,7 +17,8 @@ typedef struct Music {
char* music_path(const char *name);
bool check_music_path(const char *path);
void* load_music(const char *path, unsigned int flags);
void* load_music_begin(const char *path, unsigned int flags);
void* load_music_end(void *opaque, const char *path, unsigned int flags);
void unload_music(void *snd);
#define BGM_PATH_PREFIX "bgm/"

View file

@ -22,7 +22,7 @@ bool check_music_path(const char *path) {
return strstartswith(path, BGM_PATH_PREFIX) && audio_mixer_check_sound_path(path, true);
}
void* load_music(const char *path, unsigned int flags) {
void* load_music_begin(const char *path, unsigned int flags) {
Mix_Music *music = Mix_LoadMUS(path);
if(!music) {
@ -36,6 +36,10 @@ void* load_music(const char *path, unsigned int flags) {
return mus;
}
void* load_music_end(void *opaque, const char *path, unsigned int flags) {
return opaque;
}
void unload_music(void *vmus) {
Sound *mus = vmus;
Mix_FreeMusic((Mix_Music*)mus->impl);

View file

@ -4,5 +4,6 @@
char* music_path(const char *name) { return NULL; }
bool check_music_path(const char *path) { return NULL; }
void* load_music(const char *path, unsigned int flags) { return NULL; }
void* load_music_begin(const char *path, unsigned int flags) { return NULL; }
void* load_music_end(void *opaque, const char *path, unsigned int flags) { return NULL; }
void unload_music(void *vmus) { }

View file

@ -23,61 +23,94 @@ bool check_model_path(const char *path) {
return strendswith(path, MDL_EXTENSION);
}
void* load_model(const char *path, unsigned int flags) {
Model *m = malloc(sizeof(Model));
ObjFileData data;
unsigned int i;
typedef struct ModelLoadData {
ObjFileData *obj;
Vertex *verts;
unsigned int ioffset = _vbo.offset;
Model *model;
} ModelLoadData;
parse_obj(path, &data);
void* load_model_begin(const char *path, unsigned int flags) {
Model *m = malloc(sizeof(Model));
ObjFileData *data = malloc(sizeof(ObjFileData));
Vertex *verts;
m->fverts = data.fverts;
m->indices = calloc(data.icount, sizeof(unsigned int));
m->icount = data.icount;
parse_obj(path, data);
verts = calloc(data.icount, sizeof(Vertex));
m->fverts = data->fverts;
m->indices = calloc(data->icount, sizeof(unsigned int));
m->icount = data->icount;
#define BADREF(filename,aux,n) { log_warn("OBJ file '%s': Index %d: bad %s index reference\n", filename, n, aux); free(verts); free_obj(&data); return NULL; }
verts = calloc(data->icount, sizeof(Vertex));
memset(verts, 0, data.icount*sizeof(Vertex));
for(i = 0; i < data.icount; i++) {
#define BADREF(filename,aux,n) { \
log_warn("OBJ file '%s': Index %d: bad %s index reference\n", filename, n, aux); \
free(m->indices); \
free(m); \
free(verts); \
free_obj(data); \
free(data); \
return NULL; \
}
memset(verts, 0, data->icount*sizeof(Vertex));
for(unsigned int i = 0; i < data->icount; i++) {
int xi, ni, ti;
xi = data.indices[i][0]-1;
if(xi < 0 || xi >= data.xcount)
xi = data->indices[i][0]-1;
if(xi < 0 || xi >= data->xcount)
BADREF(path, "vertex", i);
memcpy(verts[i].x, data.xs[xi], sizeof(Vector));
memcpy(verts[i].x, data->xs[xi], sizeof(Vector));
if(data.tcount) {
ti = data.indices[i][1]-1;
if(ti < 0 || ti >= data.tcount)
if(data->tcount) {
ti = data->indices[i][1]-1;
if(ti < 0 || ti >= data->tcount)
BADREF(path, "texcoord", i);
verts[i].s = data.texcoords[ti][0];
verts[i].t = data.texcoords[ti][1];
verts[i].s = data->texcoords[ti][0];
verts[i].t = data->texcoords[ti][1];
}
if(data.ncount) {
ni = data.indices[i][2]-1;
if(ni < 0 || ni >= data.ncount)
if(data->ncount) {
ni = data->indices[i][2]-1;
if(ni < 0 || ni >= data->ncount)
BADREF(path, "normal", ni);
memcpy(verts[i].n, data.normals[ni], sizeof(Vector));
memcpy(verts[i].n, data->normals[ni], sizeof(Vector));
}
m->indices[i] = i+ioffset;
m->indices[i] = i;
}
#undef BADREF
vbo_add_verts(&_vbo, verts, data.icount);
ModelLoadData *ldata = malloc(sizeof(ModelLoadData));
ldata->obj = data;
ldata->verts = verts;
ldata->model = m;
free(verts);
free_obj(&data);
return ldata;
}
void* load_model_end(void *opaque, const char *path, unsigned int flags) {
ModelLoadData *ldata = opaque;
unsigned int ioffset = _vbo.offset;
if(!ldata) {
return NULL;
}
for(int i = 0; i < ldata->obj->icount; ++i) {
ldata->model->indices[i] += ioffset;
}
vbo_add_verts(&_vbo, ldata->verts, ldata->obj->icount);
free(ldata->verts);
free_obj(ldata->obj);
free(ldata->obj);
Model *m = ldata->model;
free(ldata);
return m;
}
@ -102,7 +135,7 @@ static void parse_obj(const char *filename, ObjFileData *data) {
return;
}
char line[256];
char line[256], *save;
Vector buf;
char mode;
int linen = 0;
@ -113,7 +146,7 @@ static void parse_obj(const char *filename, ObjFileData *data) {
linen++;
char *first;
first = strtok(line, " \n");
first = strtok_r(line, " \n", &save);
if(strcmp(first, "v") == 0)
mode = 'v';
@ -127,10 +160,11 @@ static void parse_obj(const char *filename, ObjFileData *data) {
mode = 0;
if(mode != 0 && mode != 'f') {
buf[0] = atof(strtok(NULL, " \n"));
buf[1] = atof(strtok(NULL, " \n"));
buf[0] = atof(strtok_r(NULL, " \n", &save));
char *wtf = strtok_r(NULL, " \n", &save);
buf[1] = atof(wtf);
if(mode != 't')
buf[2] = atof(strtok(NULL, " \n"));
buf[2] = atof(strtok_r(NULL, " \n", &save));
switch(mode) {
case 'v':
@ -152,7 +186,7 @@ static void parse_obj(const char *filename, ObjFileData *data) {
IVector ibuf;
memset(ibuf, 0, sizeof(ibuf));
while((segment = strtok(NULL, " \n"))) {
while((segment = strtok_r(NULL, " \n", &save))) {
seg = segment;
j++;

View file

@ -37,7 +37,8 @@ typedef struct Model {
char* model_path(const char *name);
bool check_model_path(const char *path);
void* load_model(const char *path, unsigned int flags);
void* load_model_begin(const char *path, unsigned int flags);
void* load_model_end(void *opaque, const char *path, unsigned int flags);
void unload_model(void*); // Does not delete elements from the VBO, so doing this at runtime is leaking VBO space
Model* get_model(const char *name);

View file

@ -28,10 +28,13 @@ static inline ResourceHandler* get_handler(ResourceType type) {
return resources.handlers + type;
}
static uint32_t sdlevent_finalize_load;
static void register_handler(
ResourceType type,
const char *subdir, // trailing slash assumed
ResourceLoadFunc load,
ResourceBeginLoadFunc begin_load,
ResourceEndLoadFunc end_load,
ResourceUnloadFunc unload,
ResourceNameFunc name,
ResourceFindFunc find,
@ -42,12 +45,14 @@ static void register_handler(
ResourceHandler *h = get_handler(type);
h->type = type;
h->load = load;
h->begin_load = begin_load;
h->end_load = end_load;
h->unload = unload;
h->name = name;
h->find = find;
h->check = check;
h->mapping = hashtable_new_stringkeys(tablesize);
h->async_load_data = hashtable_new_stringkeys(tablesize);
strcpy(h->subdir, subdir);
}
@ -94,9 +99,95 @@ static char* get_name(ResourceHandler *handler, const char *path) {
return resource_util_basename(handler->subdir, path);
}
static Resource* load_resource(ResourceHandler *handler, const char *path, const char *name, ResourceFlags flags) {
typedef struct ResourceAsyncLoadData {
ResourceHandler *handler;
char *path;
char *name;
ResourceFlags flags;
void *opaque;
} ResourceAsyncLoadData;
static int load_resource_async_thread(void *vdata) {
ResourceAsyncLoadData *data = vdata;
SDL_Event evt;
data->opaque = data->handler->begin_load(data->path, data->flags);
SDL_zero(evt);
evt.type = sdlevent_finalize_load;
evt.user.data1 = data;
SDL_PushEvent(&evt);
log_debug("Thread is exiting");
return 0;
}
static Resource* load_resource_finish(void *opaque, ResourceHandler *handler, const char *path, const char *name, char *allocated_path, char *allocated_name, ResourceFlags flags);
bool resource_sdl_event(SDL_Event *evt) {
if(evt->type != sdlevent_finalize_load) {
return false;
}
ResourceAsyncLoadData *data = evt->user.data1;
if(!data) {
free(data);
return true;
}
hashtable_unset(data->handler->async_load_data, data->name);
load_resource_finish(data->opaque, data->handler, data->path, data->name, data->path, data->name, data->flags);
free(data);
return true;
}
static void load_resource_async(ResourceHandler *handler, char *path, char *name, ResourceFlags flags) {
log_debug("Loading %s '%s' asynchronously", resource_type_names[handler->type], name);
ResourceAsyncLoadData *data = malloc(sizeof(ResourceAsyncLoadData));
hashtable_set_string(handler->async_load_data, name, data);
data->handler = handler;
data->path = path;
data->name = name;
data->flags = flags;
SDL_DetachThread(SDL_CreateThread(load_resource_async_thread, __func__, data));
}
static void update_async_load_state(void) {
SDL_Event evt;
while(SDL_PeepEvents(&evt, 1, SDL_GETEVENT, sdlevent_finalize_load, sdlevent_finalize_load)) {
resource_sdl_event(&evt);
}
}
static bool resource_check_async_load(ResourceHandler *handler, const char *name) {
update_async_load_state();
ResourceAsyncLoadData *data = hashtable_get_string(handler->async_load_data, name);
return data;
}
static void resource_wait_for_async_load(ResourceHandler *handler, const char *name) {
// XXX: is there a better way than a busy loop?
while(resource_check_async_load(handler, name));
}
static void resource_wait_for_all_async_loads(ResourceHandler *handler) {
char *key;
hashtable_lock(handler->async_load_data);
HashtableIterator *i = hashtable_iter(handler->async_load_data);
while(hashtable_iter_next(i, (void**)&key, NULL)) {
resource_check_async_load(handler, key);
}
hashtable_unlock(handler->async_load_data);
}
static Resource* load_resource(ResourceHandler *handler, const char *path, const char *name, ResourceFlags flags, bool async) {
Resource *res;
void *raw;
const char *typename = resource_type_names[handler->type];
char *allocated_path = NULL;
@ -122,6 +213,14 @@ static Resource* load_resource(ResourceHandler *handler, const char *path, const
assert(handler->check(path));
if(async) {
if(resource_check_async_load(handler, name)) {
return NULL;
}
} else {
resource_wait_for_async_load(handler, name);
}
if(!(flags & RESF_OVERRIDE)) {
res = hashtable_get_string(handler->mapping, name);
@ -132,7 +231,20 @@ static Resource* load_resource(ResourceHandler *handler, const char *path, const
}
}
raw = handler->load(path, flags);
if(async) {
// these will be freed when loading is done
path = allocated_path ? allocated_path : strdup(path);
name = allocated_name ? allocated_name : strdup(name);
load_resource_async(handler, (char*)path, (char*)name, flags);
return NULL;
}
return load_resource_finish(handler->begin_load(path, flags), handler, path, name, allocated_path, allocated_name, flags);
}
static Resource* load_resource_finish(void *opaque, ResourceHandler *handler, const char *path, const char *name, char *allocated_path, char *allocated_name, ResourceFlags flags) {
const char *typename = resource_type_names[handler->type];
void *raw = handler->end_load(opaque, path, flags);
if(!raw) {
name = name ? name : "<name unknown>";
@ -150,7 +262,7 @@ static Resource* load_resource(ResourceHandler *handler, const char *path, const
return NULL;
}
res = insert_resource(handler->type, name, raw, flags, path);
Resource *res = insert_resource(handler->type, name, raw, flags, path);
free(allocated_path);
free(allocated_name);
@ -160,6 +272,7 @@ static Resource* load_resource(ResourceHandler *handler, const char *path, const
Resource* get_resource(ResourceType type, const char *name, ResourceFlags flags) {
ResourceHandler *handler = get_handler(type);
resource_wait_for_async_load(handler, name);
Resource *res = hashtable_get_string(handler->mapping, name);
if(!res || flags & RESF_OVERRIDE) {
@ -171,7 +284,7 @@ Resource* get_resource(ResourceType type, const char *name, ResourceFlags flags)
}
}
res = load_resource(handler, NULL, name, flags);
res = load_resource(handler, NULL, name, flags, false);
}
if(res && flags & RESF_PERMANENT && !(res->flags & RESF_PERMANENT)) {
@ -183,9 +296,17 @@ Resource* get_resource(ResourceType type, const char *name, ResourceFlags flags)
}
void preload_resource(ResourceType type, const char *name, ResourceFlags flags) {
if(!getenvint("TAISEI_NOPRELOAD")) {
get_resource(type, name, flags | RESF_PRELOAD);
if(getenvint("TAISEI_NOPRELOAD"))
return;
ResourceHandler *handler = get_handler(type);
if(hashtable_get_string(handler->mapping, name) ||
hashtable_get_string(handler->async_load_data, name)) {
return;
}
load_resource(handler, NULL, name, flags | RESF_PRELOAD, !getenvint("TAISEI_NOASYNC"));
}
void preload_resources(ResourceType type, ResourceFlags flags, const char *firstname, ...) {
@ -203,27 +324,27 @@ void init_resources(void) {
// hashtable sizes were carefully pulled out of my ass to reduce collisions a bit
register_handler(
RES_TEXTURE, TEX_PATH_PREFIX, load_texture, (ResourceUnloadFunc)free_texture, NULL, texture_path, check_texture_path, 227
RES_TEXTURE, TEX_PATH_PREFIX, load_texture_begin, load_texture_end, (ResourceUnloadFunc)free_texture, NULL, texture_path, check_texture_path, 227
);
register_handler(
RES_ANIM, ANI_PATH_PREFIX, load_animation, free, NULL, animation_path, check_animation_path, 23
RES_ANIM, ANI_PATH_PREFIX, load_animation_begin, load_animation_end, free, NULL, animation_path, check_animation_path, 23
);
register_handler(
RES_SHADER, SHA_PATH_PREFIX, load_shader_file, unload_shader, NULL, shader_path, check_shader_path, 29
RES_SHADER, SHA_PATH_PREFIX, load_shader_begin, load_shader_end, unload_shader, NULL, shader_path, check_shader_path, 29
);
register_handler(
RES_MODEL, MDL_PATH_PREFIX, load_model, unload_model, NULL, model_path, check_model_path, 17
RES_MODEL, MDL_PATH_PREFIX, load_model_begin, load_model_end, unload_model, NULL, model_path, check_model_path, 17
);
register_handler(
RES_SFX, SFX_PATH_PREFIX, load_sound, unload_sound, NULL, sound_path, check_sound_path, 16
RES_SFX, SFX_PATH_PREFIX, load_sound_begin, load_sound_end, unload_sound, NULL, sound_path, check_sound_path, 16
);
register_handler(
RES_BGM, BGM_PATH_PREFIX, load_music, unload_music, NULL, music_path, check_music_path, 16
RES_BGM, BGM_PATH_PREFIX, load_music_begin, load_music_end, unload_music, NULL, music_path, check_music_path, 16
);
}
@ -270,8 +391,7 @@ void free_resources(bool all) {
for(ResourceType type = 0; type < RES_NUMTYPES; ++type) {
ResourceHandler *handler = get_handler(type);
if(!handler->mapping)
continue;
resource_wait_for_all_async_loads(handler);
char *name;
Resource *res;

View file

@ -57,9 +57,15 @@ typedef char* (*ResourceFindFunc)(const char *name);
// Tells whether the resource handler should attempt to load a file, specified by a path.
typedef bool (*ResourceCheckFunc)(const char *path);
// Loads a resource at path and returns a pointer to it.
// Must return NULL and not crash the program on failure.
typedef void* (*ResourceLoadFunc)(const char *path, unsigned int flags);
// Begins loading a resource specified by path.
// May be called asynchronously.
// The return value is not interpreted in any way, it's just passed to the corresponding ResourceEndLoadFunc later.
typedef void* (*ResourceBeginLoadFunc)(const char *path, unsigned int flags);
// Finishes loading a resource and returns a pointer to it.
// Will be called from the main thread only.
// On failure, must return NULL and not crash the program.
typedef void* (*ResourceEndLoadFunc)(void *opaque, const char *path, unsigned int flags);
// Unloads a resource, freeing all allocated to it memory.
typedef void (*ResourceUnloadFunc)(void *res);
@ -69,9 +75,11 @@ typedef struct ResourceHandler {
ResourceNameFunc name;
ResourceFindFunc find;
ResourceCheckFunc check;
ResourceLoadFunc load;
ResourceBeginLoadFunc begin_load;
ResourceEndLoadFunc end_load;
ResourceUnloadFunc unload;
Hashtable *mapping;
Hashtable *async_load_data;
char subdir[32];
} ResourceHandler;
@ -113,4 +121,6 @@ const char* resource_util_filename(const char *path);
void print_resource_hashtables(void);
bool resource_sdl_event(SDL_Event *evt);
#endif

View file

@ -17,7 +17,8 @@ typedef struct Sound {
char* sound_path(const char *name);
bool check_sound_path(const char *path);
void* load_sound(const char *path, unsigned int flags);
void* load_sound_begin(const char *path, unsigned int flags);
void* load_sound_end(void *opaque, const char *path, unsigned int flags);
void unload_sound(void *snd);
#define SFX_PATH_PREFIX "sfx/"

View file

@ -22,7 +22,7 @@ bool check_sound_path(const char *path) {
return strstartswith(path, SFX_PATH_PREFIX) && audio_mixer_check_sound_path(path, false);
}
void* load_sound(const char *path, unsigned int flags) {
void* load_sound_begin(const char *path, unsigned int flags) {
Mix_Chunk *sound = Mix_LoadWAV(path);
if(!sound) {
@ -36,6 +36,10 @@ void* load_sound(const char *path, unsigned int flags) {
return snd;
}
void* load_sound_end(void *opaque, const char *path, unsigned int flags) {
return opaque;
}
void unload_sound(void *vsnd) {
Sound *snd = vsnd;
Mix_FreeChunk((Mix_Chunk*)snd->impl);

View file

@ -4,5 +4,6 @@
char* sound_path(const char *name) { return NULL; }
bool check_sound_path(const char *path) { return NULL; }
void* load_sound(const char *path, unsigned int flags) { return NULL; }
void* load_sound_begin(const char *path, unsigned int flags) { return NULL; }
void* load_sound_end(void *opaque, const char *path, unsigned int flags) { return NULL; }
void unload_sound(void *vmus) { }

View file

@ -38,7 +38,13 @@ bool check_shader_path(const char *path) {
static Shader* load_shader(const char *vheader, const char *fheader, const char *vtext, const char *ftext);
void* load_shader_file(const char *path, unsigned int flags) {
typedef struct ShaderLoadData {
char *text;
char *vtext;
char *ftext;
} ShaderLoadData;
void* load_shader_begin(const char *path, unsigned int flags) {
char *text, *vtext, *ftext, *delim;
text = read_all(path, NULL);
@ -55,9 +61,25 @@ void* load_shader_file(const char *path, unsigned int flags) {
*delim = 0;
ftext = delim + SHA_DELIM_SIZE;
Shader *sha = load_shader(NULL, NULL, vtext, ftext);
ShaderLoadData *data = malloc(sizeof(ShaderLoadData));
data->text = text;
data->vtext = vtext;
data->ftext = ftext;
free(text);
return data;
}
void* load_shader_end(void *opaque, const char *path, unsigned int flags) {
ShaderLoadData *data = opaque;
if(!data) {
return NULL;
}
Shader *sha = load_shader(NULL, NULL, data->vtext, data->ftext);
free(data->text);
free(data);
return sha;
}
@ -210,7 +232,7 @@ static void cache_uniforms(Shader *sha) {
}
#ifdef DEBUG_GL
hashtable_print_stringkeys(sha->uniforms);
// hashtable_print_stringkeys(sha->uniforms);
#endif
}

View file

@ -19,7 +19,8 @@ typedef struct Shader {
char* shader_path(const char *name);
bool check_shader_path(const char *path);
void* load_shader_file(const char *path, unsigned int flags);
void* load_shader_begin(const char *path, unsigned int flags);
void* load_shader_end(void *opaque, const char *path, unsigned int flags);
void unload_shader(void *vsha);
void load_shader_snippets(const char *filename, const char *prefix, unsigned int flags);

View file

@ -24,10 +24,14 @@ bool check_texture_path(const char *path) {
return strendswith(path, TEX_EXTENSION);
}
void* load_texture(const char *path, unsigned int flags) {
SDL_Surface *surface = load_png(path);
void* load_texture_begin(const char *path, unsigned int flags) {
return load_png(path);
}
if(surface == NULL) {
void* load_texture_end(void *opaque, const char *path, unsigned int flags) {
SDL_Surface *surface = opaque;
if(!surface) {
return NULL;
}

View file

@ -21,7 +21,8 @@ struct Texture {
};
char* texture_path(const char *name);
void* load_texture(const char *path, unsigned int flags);
void* load_texture_begin(const char *path, unsigned int flags);
void* load_texture_end(void *opaque, const char *path, unsigned int flags);
bool check_texture_path(const char *path);
void load_sdl_surf(SDL_Surface *surface, Texture *texture);

View file

@ -129,6 +129,41 @@ void strip_trailing_slashes(char *buf) {
*c = 0;
}
/*
* public domain strtok_r() by Charlie Gordon
*
* from comp.lang.c 9/14/2007
*
* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
*
* (Declaration that it's public domain):
* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
*/
char* strtok_r(char *str, const char *delim, char **nextp) {
char *ret;
if(str == NULL) {
str = *nextp;
}
str += strspn(str, delim);
if(*str == '\0') {
return NULL;
}
ret = str;
str += strcspn(str, delim);
if(*str) {
*str++ = '\0';
}
*nextp = str;
return ret;
}
//
// math utils
//

View file

@ -38,6 +38,7 @@ char* strjoin(const char *first, ...) __attribute__((sentinel));
char* vstrfmt(const char *fmt, va_list args);
char* strfmt(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void strip_trailing_slashes(char *buf);
char* strtok_r(char *str, const char *delim, char **nextp);
#undef strdup
#define strdup SDL_strdup
@ -148,4 +149,8 @@ int printf(const char*, ...) __attribute__((deprecated(
int fprintf(FILE*, const char*, ...) __attribute__((deprecated(
"Use log_warn instead (or SDL_RWops if you want to write to a file)")));
#undef strtok
char* strtok() __attribute__((deprecated(
"Use strtok_r instead")));
#endif