WIP async loading
This commit is contained in:
parent
15c2472336
commit
df90733c8f
23 changed files with 405 additions and 89 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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/"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) { }
|
||||
|
|
|
@ -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++;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) { }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
35
src/util.c
35
src/util.c
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue