resource: partial support for resource reloading
This commit is contained in:
parent
1886e95a66
commit
b39c9ba78e
21 changed files with 511 additions and 56 deletions
|
@ -58,6 +58,7 @@
|
|||
CONFIGDEF_KEYBINDING(KEY_RESTART, "key_restart", SDL_SCANCODE_F2) \
|
||||
CONFIGDEF_KEYBINDING(KEY_HITAREAS, "key_hitareas", SDL_SCANCODE_H) \
|
||||
CONFIGDEF_KEYBINDING(KEY_TOGGLE_AUDIO, "key_toggle_audio", SDL_SCANCODE_M) \
|
||||
CONFIGDEF_KEYBINDING(KEY_RELOAD_RESOURCES, "key_reload_resources", SDL_SCANCODE_F5) \
|
||||
|
||||
|
||||
#define GPKEYDEFS \
|
||||
|
|
|
@ -526,5 +526,10 @@ static bool events_handler_hotkeys(SDL_Event *event, void *arg) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if(scan == config_get_int(CONFIG_KEY_RELOAD_RESOURCES)) {
|
||||
reload_all_resources();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -356,6 +356,10 @@ const char* r_shader_object_get_debug_label(ShaderObject *shobj) {
|
|||
return B.shader_object_get_debug_label(shobj);
|
||||
}
|
||||
|
||||
bool r_shader_object_transfer(ShaderObject *dst, ShaderObject *src) {
|
||||
return B.shader_object_transfer(dst, src);
|
||||
}
|
||||
|
||||
ShaderProgram* r_shader_program_link(uint num_objects, ShaderObject *shobjs[num_objects]) {
|
||||
return B.shader_program_link(num_objects, shobjs);
|
||||
}
|
||||
|
@ -372,6 +376,10 @@ const char* r_shader_program_get_debug_label(ShaderProgram *prog) {
|
|||
return B.shader_program_get_debug_label(prog);
|
||||
}
|
||||
|
||||
bool r_shader_program_transfer(ShaderProgram *dst, ShaderProgram *src) {
|
||||
return B.shader_program_transfer(dst, src);
|
||||
}
|
||||
|
||||
void r_shader_ptr(ShaderProgram *prog) {
|
||||
_r_state_touch_shader();
|
||||
B.shader(prog);
|
||||
|
@ -462,6 +470,10 @@ void r_texture_destroy(Texture *tex) {
|
|||
B.texture_destroy(tex);
|
||||
}
|
||||
|
||||
bool r_texture_transfer(Texture *dst, Texture *src) {
|
||||
return B.texture_transfer(dst, src);
|
||||
}
|
||||
|
||||
bool r_texture_type_query(TextureType type, TextureFlags flags, PixmapFormat pxfmt, PixmapOrigin pxorigin, TextureTypeQueryResult *result) {
|
||||
return B.texture_type_query(type, flags, pxfmt, pxorigin, result);
|
||||
}
|
||||
|
|
|
@ -539,11 +539,13 @@ ShaderObject* r_shader_object_compile(ShaderSource *source) attr_nonnull(1);
|
|||
void r_shader_object_destroy(ShaderObject *shobj) attr_nonnull(1);
|
||||
void r_shader_object_set_debug_label(ShaderObject *shobj, const char *label) attr_nonnull(1);
|
||||
const char* r_shader_object_get_debug_label(ShaderObject *shobj) attr_nonnull(1);
|
||||
bool r_shader_object_transfer(ShaderObject *dst, ShaderObject *src) attr_nonnull_all;
|
||||
|
||||
ShaderProgram* r_shader_program_link(uint num_objects, ShaderObject *shobjs[num_objects]) attr_nonnull(2);
|
||||
void r_shader_program_destroy(ShaderProgram *prog);
|
||||
void r_shader_program_set_debug_label(ShaderProgram *prog, const char *label) attr_nonnull(1);
|
||||
const char* r_shader_program_get_debug_label(ShaderProgram *prog) attr_nonnull(1);
|
||||
bool r_shader_program_transfer(ShaderProgram *dst, ShaderProgram *src) attr_nonnull_all;
|
||||
|
||||
void r_shader_ptr(ShaderProgram *prog) attr_nonnull(1);
|
||||
ShaderProgram* r_shader_current(void) attr_returns_nonnull;
|
||||
|
@ -737,6 +739,7 @@ bool r_texture_dump(Texture *tex, uint mipmap, uint layer, Pixmap *dst) attr_non
|
|||
void r_texture_invalidate(Texture *tex) attr_nonnull(1);
|
||||
void r_texture_clear(Texture *tex, const Color *clr) attr_nonnull(1, 2);
|
||||
void r_texture_destroy(Texture *tex) attr_nonnull(1);
|
||||
bool r_texture_transfer(Texture *dst, Texture *src) attr_nonnull(1);
|
||||
|
||||
bool r_texture_type_query(TextureType type, TextureFlags flags, PixmapFormat pxfmt, PixmapOrigin pxorigin, TextureTypeQueryResult *result) attr_nodiscard;
|
||||
const char *r_texture_type_name(TextureType type);
|
||||
|
|
|
@ -49,11 +49,13 @@ typedef struct RendererFuncs {
|
|||
void (*shader_object_destroy)(ShaderObject *shobj);
|
||||
void (*shader_object_set_debug_label)(ShaderObject *shobj, const char *label);
|
||||
const char* (*shader_object_get_debug_label)(ShaderObject *shobj);
|
||||
bool (*shader_object_transfer)(ShaderObject *dst, ShaderObject *src);
|
||||
|
||||
ShaderProgram* (*shader_program_link)(uint num_objects, ShaderObject *shobjs[num_objects]);
|
||||
void (*shader_program_destroy)(ShaderProgram *prog);
|
||||
void (*shader_program_set_debug_label)(ShaderProgram *prog, const char *label);
|
||||
const char* (*shader_program_get_debug_label)(ShaderProgram *prog);
|
||||
bool (*shader_program_transfer)(ShaderProgram *dst, ShaderProgram *src);
|
||||
|
||||
void (*shader)(ShaderProgram *prog);
|
||||
ShaderProgram* (*shader_current)(void);
|
||||
|
@ -76,6 +78,7 @@ typedef struct RendererFuncs {
|
|||
bool (*texture_dump)(Texture *tex, uint mipmap, uint layer, Pixmap *dst);
|
||||
void (*texture_clear)(Texture *tex, const Color *clr);
|
||||
bool (*texture_type_query)(TextureType type, TextureFlags flags, PixmapFormat pxfmt, PixmapOrigin pxorigin, TextureTypeQueryResult *result);
|
||||
bool (*texture_transfer)(Texture *dst, Texture *src);
|
||||
|
||||
Framebuffer* (*framebuffer_create)(void);
|
||||
const char* (*framebuffer_get_debug_label)(Framebuffer *framebuffer);
|
||||
|
|
|
@ -1035,6 +1035,21 @@ void gl33_texture_deleted(Texture *tex) {
|
|||
}
|
||||
}
|
||||
|
||||
void gl33_texture_pointer_renamed(Texture *pold, Texture *pnew) {
|
||||
_r_sprite_batch_texture_deleted(pold);
|
||||
gl33_uniforms_handle_texture_pointer_renamed(pold, pnew);
|
||||
|
||||
for(TextureUnit *unit = R.texunits.array; unit < R.texunits.array + R.texunits.limit; ++unit) {
|
||||
if(unit->pending == pold) {
|
||||
unit->pending = pnew;
|
||||
}
|
||||
|
||||
if(unit->active == pold) {
|
||||
unit->active = pnew;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gl33_framebuffer_deleted(Framebuffer *fb) {
|
||||
if(R.framebuffer.pending == fb) {
|
||||
R.framebuffer.pending = NULL;
|
||||
|
@ -1390,10 +1405,12 @@ RendererBackend _r_backend_gl33 = {
|
|||
.shader_object_destroy = gl33_shader_object_destroy,
|
||||
.shader_object_set_debug_label = gl33_shader_object_set_debug_label,
|
||||
.shader_object_get_debug_label = gl33_shader_object_get_debug_label,
|
||||
.shader_object_transfer = gl33_shader_object_transfer,
|
||||
.shader_program_link = gl33_shader_program_link,
|
||||
.shader_program_destroy = gl33_shader_program_destroy,
|
||||
.shader_program_set_debug_label = gl33_shader_program_set_debug_label,
|
||||
.shader_program_get_debug_label = gl33_shader_program_get_debug_label,
|
||||
.shader_program_transfer = gl33_shader_program_transfer,
|
||||
.shader = gl33_shader,
|
||||
.shader_current = gl33_shader_current,
|
||||
.shader_uniform = gl33_shader_uniform,
|
||||
|
@ -1413,6 +1430,7 @@ RendererBackend _r_backend_gl33 = {
|
|||
.texture_clear = gl33_texture_clear,
|
||||
.texture_type_query = gl33_texture_type_query,
|
||||
.texture_dump = gl33_texture_dump,
|
||||
.texture_transfer = gl33_texture_transfer,
|
||||
.framebuffer_create = gl33_framebuffer_create,
|
||||
.framebuffer_destroy = gl33_framebuffer_destroy,
|
||||
.framebuffer_attach = gl33_framebuffer_attach,
|
||||
|
|
|
@ -83,4 +83,6 @@ void gl33_texture_deleted(Texture *tex);
|
|||
void gl33_framebuffer_deleted(Framebuffer *fb);
|
||||
void gl33_shader_deleted(ShaderProgram *prog);
|
||||
|
||||
void gl33_texture_pointer_renamed(Texture *pold, Texture *pnew);
|
||||
|
||||
extern RendererBackend _r_backend_gl33;
|
||||
|
|
|
@ -58,7 +58,7 @@ static void print_info_log(GLuint shader) {
|
|||
}
|
||||
}
|
||||
|
||||
ShaderObject* gl33_shader_object_compile(ShaderSource *source) {
|
||||
ShaderObject *gl33_shader_object_compile(ShaderSource *source) {
|
||||
assert(r_shader_language_supported(&source->lang, NULL));
|
||||
|
||||
GLuint gl_handle = glCreateShader(
|
||||
|
@ -100,12 +100,16 @@ ShaderObject* gl33_shader_object_compile(ShaderSource *source) {
|
|||
if(status) {
|
||||
uint nattribs = source->meta.glsl.num_attributes;
|
||||
|
||||
shobj = calloc(1, sizeof(*shobj) + sizeof(GLSLAttribute) * nattribs);
|
||||
shobj = calloc(1, sizeof(*shobj));
|
||||
shobj->gl_handle = gl_handle;
|
||||
shobj->stage = source->stage;
|
||||
shobj->num_attribs = nattribs;
|
||||
snprintf(shobj->debug_label, sizeof(shobj->debug_label), "Shader object #%i", gl_handle);
|
||||
|
||||
if(nattribs > 0) {
|
||||
shobj->attribs = calloc(nattribs, sizeof(*shobj->attribs));
|
||||
}
|
||||
|
||||
for(uint i = 0; i < nattribs; ++i) {
|
||||
GLSLAttribute *a = source->meta.glsl.attributes + i;
|
||||
shobj->attribs[i].name = strdup(a->name);
|
||||
|
@ -127,6 +131,7 @@ void gl33_shader_object_destroy(ShaderObject *shobj) {
|
|||
free(shobj->attribs[i].name);
|
||||
}
|
||||
|
||||
free(shobj->attribs);
|
||||
free(shobj);
|
||||
}
|
||||
|
||||
|
@ -134,6 +139,27 @@ void gl33_shader_object_set_debug_label(ShaderObject *shobj, const char *label)
|
|||
glcommon_set_debug_label(shobj->debug_label, "Shader object", GL_SHADER, shobj->gl_handle, label);
|
||||
}
|
||||
|
||||
const char* gl33_shader_object_get_debug_label(ShaderObject *shobj) {
|
||||
const char *gl33_shader_object_get_debug_label(ShaderObject *shobj) {
|
||||
return shobj->debug_label;
|
||||
}
|
||||
|
||||
bool gl33_shader_object_transfer(ShaderObject *dst, ShaderObject *src) {
|
||||
if(UNLIKELY(dst->stage != src->stage)) {
|
||||
log_error("Shader object changed stage");
|
||||
return false;
|
||||
}
|
||||
|
||||
glDeleteShader(dst->gl_handle);
|
||||
|
||||
uint nattribs = dst->num_attribs;
|
||||
|
||||
for(uint i = 0; i < nattribs; ++i) {
|
||||
free(dst->attribs[i].name);
|
||||
}
|
||||
|
||||
free(dst->attribs);
|
||||
*dst = *src;
|
||||
free(src);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -17,12 +17,13 @@ struct ShaderObject {
|
|||
ShaderStage stage;
|
||||
char debug_label[R_DEBUG_LABEL_SIZE];
|
||||
uint num_attribs;
|
||||
GLSLAttribute attribs[];
|
||||
GLSLAttribute *attribs;
|
||||
};
|
||||
|
||||
bool gl33_shader_language_supported(const ShaderLangInfo *lang, ShaderLangInfo *out_alternative);
|
||||
|
||||
ShaderObject* gl33_shader_object_compile(ShaderSource *source);
|
||||
ShaderObject *gl33_shader_object_compile(ShaderSource *source);
|
||||
void gl33_shader_object_destroy(ShaderObject *shobj);
|
||||
void gl33_shader_object_set_debug_label(ShaderObject *shobj, const char *label);
|
||||
const char* gl33_shader_object_get_debug_label(ShaderObject *shobj);
|
||||
const char *gl33_shader_object_get_debug_label(ShaderObject *shobj);
|
||||
bool gl33_shader_object_transfer(ShaderObject *dst, ShaderObject *src);
|
||||
|
|
|
@ -468,9 +468,13 @@ static bool cache_uniforms(ShaderProgram *prog) {
|
|||
void gl33_unref_texture_from_samplers(Texture *tex) {
|
||||
for(Uniform *u = sampler_uniforms; u; u = u->next) {
|
||||
assert(UNIFORM_TYPE_IS_SAMPLER(u->type));
|
||||
assert(u->textures != NULL);
|
||||
|
||||
if(!u->textures) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for(Texture **slot = u->textures; slot < u->textures + u->array_size; ++slot) {
|
||||
assert(slot != NULL);
|
||||
if(*slot == tex) {
|
||||
*slot = NULL;
|
||||
}
|
||||
|
@ -478,6 +482,19 @@ void gl33_unref_texture_from_samplers(Texture *tex) {
|
|||
}
|
||||
}
|
||||
|
||||
void gl33_uniforms_handle_texture_pointer_renamed(Texture *pold, Texture *pnew) {
|
||||
for(Uniform *u = sampler_uniforms; u; u = u->next) {
|
||||
assert(UNIFORM_TYPE_IS_SAMPLER(u->type));
|
||||
|
||||
for(Texture **slot = u->textures; slot < u->textures + u->array_size; ++slot) {
|
||||
assert(slot != NULL);
|
||||
if(*slot == pold) {
|
||||
*slot = pnew;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_info_log(GLuint prog) {
|
||||
GLint len = 0, alen = 0;
|
||||
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
|
||||
|
@ -561,3 +578,113 @@ void gl33_shader_program_set_debug_label(ShaderProgram *prog, const char *label)
|
|||
const char *gl33_shader_program_get_debug_label(ShaderProgram *prog) {
|
||||
return prog->debug_label;
|
||||
}
|
||||
|
||||
bool gl33_shader_program_transfer(ShaderProgram *dst, ShaderProgram *src) {
|
||||
ht_ptr2ptr_t old_new_map; // bidirectional
|
||||
ht_create(&old_new_map);
|
||||
|
||||
ht_str2ptr_iter_t iter;
|
||||
ht_iter_begin(&src->uniforms, &iter);
|
||||
|
||||
bool fail = false;
|
||||
|
||||
for(; iter.has_data; ht_iter_next(&iter)) {
|
||||
Uniform *unew = NOT_NULL(iter.value);
|
||||
Uniform *uold = ht_get(&dst->uniforms, iter.key, NULL);
|
||||
|
||||
if(!uold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(unew->type != uold->type || unew->elem_size != uold->elem_size) {
|
||||
log_error(
|
||||
"Can't update shader program '%s': uniform %s changed type",
|
||||
dst->debug_label, iter.key
|
||||
);
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
ht_set(&old_new_map, uold, unew);
|
||||
ht_set(&old_new_map, unew, uold);
|
||||
}
|
||||
|
||||
ht_iter_end(&iter);
|
||||
|
||||
if(fail) {
|
||||
ht_destroy(&old_new_map);
|
||||
gl33_shader_program_destroy(src);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update existing uniforms
|
||||
|
||||
ht_iter_begin(&dst->uniforms, &iter);
|
||||
|
||||
for(; iter.has_data; ht_iter_next(&iter)) {
|
||||
Uniform *uold = NOT_NULL(iter.value);
|
||||
Uniform *unew = ht_get(&old_new_map, uold, NULL);
|
||||
|
||||
free(uold->textures);
|
||||
free(uold->cache.pending);
|
||||
free(uold->cache.commited);
|
||||
|
||||
if(unew) {
|
||||
uold->textures = unew->textures;
|
||||
assert(uold->elem_size == unew->elem_size);
|
||||
uold->array_size = unew->array_size;
|
||||
uold->location = unew->location;
|
||||
assert(uold->type == unew->type);
|
||||
uold->size_uniform = ht_get(&old_new_map, unew->size_uniform, unew->size_uniform);
|
||||
uold->cache = unew->cache;
|
||||
|
||||
if(UNIFORM_TYPE_IS_SAMPLER(unew->type)) {
|
||||
list_unlink(&sampler_uniforms, unew);
|
||||
}
|
||||
|
||||
ht_unset(&src->uniforms, iter.key);
|
||||
free(unew);
|
||||
} else {
|
||||
// Deactivate, but keep the object around, because user code may be referencing it.
|
||||
// We also need to keep type information, in case the uniform gets re-introduced.
|
||||
uold->location = INVALID_UNIFORM_LOCATION;
|
||||
uold->size_uniform = NULL;
|
||||
uold->array_size = 0;
|
||||
uold->textures = NULL;
|
||||
uold->cache.pending = NULL;
|
||||
uold->cache.commited = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ht_iter_end(&iter);
|
||||
|
||||
// Add new uniforms
|
||||
|
||||
ht_iter_begin(&src->uniforms, &iter);
|
||||
|
||||
for(; iter.has_data; ht_iter_next(&iter)) {
|
||||
Uniform *unew = NOT_NULL(iter.value);
|
||||
assert(ht_get(&old_new_map, unew, NULL) == NULL);
|
||||
assert(ht_get(&dst->uniforms, iter.key, NULL) == NULL);
|
||||
|
||||
unew->prog = dst;
|
||||
unew->size_uniform = ht_get(&old_new_map, unew->size_uniform, unew->size_uniform);
|
||||
ht_set(&dst->uniforms, iter.key, unew);
|
||||
}
|
||||
|
||||
ht_iter_end(&iter);
|
||||
|
||||
dst->gl_handle = src->gl_handle;
|
||||
memcpy(dst->debug_label, src->debug_label, sizeof(dst->debug_label));
|
||||
|
||||
for(int i = 0; i < ARRAY_SIZE(dst->magic_uniforms); ++i) {
|
||||
Uniform *unew = src->magic_uniforms[i];
|
||||
dst->magic_uniforms[i] = ht_get(&old_new_map, unew, unew);
|
||||
}
|
||||
|
||||
ht_destroy(&old_new_map);
|
||||
ht_destroy(&src->uniforms);
|
||||
free(src);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ struct ShaderProgram {
|
|||
char debug_label[R_DEBUG_LABEL_SIZE];
|
||||
};
|
||||
|
||||
#define INVALID_UNIFORM_LOCATION 0xffffffff
|
||||
|
||||
struct Uniform {
|
||||
// these are for sampler uniforms
|
||||
LIST_INTERFACE(Uniform);
|
||||
|
@ -71,3 +73,5 @@ Uniform *gl33_shader_uniform(ShaderProgram *prog, const char *uniform_name, hash
|
|||
UniformType gl33_uniform_type(Uniform *uniform);
|
||||
void gl33_uniform(Uniform *uniform, uint offset, uint count, const void *data);
|
||||
void gl33_unref_texture_from_samplers(Texture *tex);
|
||||
void gl33_uniforms_handle_texture_pointer_renamed(Texture *pold, Texture *pnew);
|
||||
bool gl33_shader_program_transfer(ShaderProgram *dst, ShaderProgram *src);
|
||||
|
|
|
@ -622,3 +622,12 @@ bool gl33_texture_dump(Texture *tex, uint mipmap, uint layer, Pixmap *dst) {
|
|||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool gl33_texture_transfer(Texture *dst, Texture *src) {
|
||||
gl33_texture_deleted(dst);
|
||||
glDeleteTextures(1, &dst->gl_handle);
|
||||
*dst = *src;
|
||||
gl33_texture_pointer_renamed(src, dst);
|
||||
free(src);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -44,3 +44,4 @@ void gl33_texture_destroy(Texture *tex);
|
|||
bool gl33_texture_type_query(TextureType type, TextureFlags flags, PixmapFormat pxfmt, PixmapOrigin pxorigin, TextureTypeQueryResult *result);
|
||||
bool gl33_texture_sampler_compatible(Texture *tex, UniformType sampler_type) attr_nonnull(1);
|
||||
bool gl33_texture_dump(Texture *tex, uint mipmap, uint layer, Pixmap *dst);
|
||||
bool gl33_texture_transfer(Texture *dst, Texture *src);
|
||||
|
|
|
@ -46,11 +46,13 @@ static ShaderObject* null_shader_object_compile(ShaderSource *source) { return (
|
|||
static void null_shader_object_destroy(ShaderObject *shobj) { }
|
||||
static void null_shader_object_set_debug_label(ShaderObject *shobj, const char *label) { }
|
||||
static const char* null_shader_object_get_debug_label(ShaderObject *shobj) { return "Null shader object"; }
|
||||
static bool null_shader_object_transfer(ShaderObject *dst, ShaderObject *src) { return true; }
|
||||
|
||||
static ShaderProgram* null_shader_program_link(uint num_objects, ShaderObject *shobjs[num_objects]) { return (void*)&placeholder; }
|
||||
static void null_shader_program_destroy(ShaderProgram *prog) { }
|
||||
static void null_shader_program_set_debug_label(ShaderProgram *prog, const char *label) { }
|
||||
static const char* null_shader_program_get_debug_label(ShaderProgram *prog) { return "Null shader program"; }
|
||||
static bool null_shader_program_transfer(ShaderProgram *dst, ShaderProgram *src) { return true; }
|
||||
|
||||
static void null_shader(ShaderProgram *prog) { }
|
||||
static ShaderProgram* null_shader_current(void) { return (void*)&placeholder; }
|
||||
|
@ -101,6 +103,7 @@ static bool null_texture_type_query(TextureType type, TextureFlags flags, Pixmap
|
|||
|
||||
return true;
|
||||
}
|
||||
static bool null_texture_transfer(Texture *dst, Texture *src) { return true; }
|
||||
|
||||
static FloatRect default_fb_viewport = { 0, 0, 800, 600 };
|
||||
|
||||
|
@ -208,10 +211,12 @@ RendererBackend _r_backend_null = {
|
|||
.shader_object_destroy = null_shader_object_destroy,
|
||||
.shader_object_set_debug_label = null_shader_object_set_debug_label,
|
||||
.shader_object_get_debug_label = null_shader_object_get_debug_label,
|
||||
.shader_object_transfer = null_shader_object_transfer,
|
||||
.shader_program_link = null_shader_program_link,
|
||||
.shader_program_destroy = null_shader_program_destroy,
|
||||
.shader_program_set_debug_label = null_shader_program_set_debug_label,
|
||||
.shader_program_get_debug_label = null_shader_program_get_debug_label,
|
||||
.shader_program_transfer = null_shader_program_transfer,
|
||||
.shader = null_shader,
|
||||
.shader_current = null_shader_current,
|
||||
.shader_uniform = null_shader_uniform,
|
||||
|
@ -231,6 +236,7 @@ RendererBackend _r_backend_null = {
|
|||
.texture_dump = null_texture_dump,
|
||||
.texture_clear = null_texture_clear,
|
||||
.texture_type_query = null_texture_type_query,
|
||||
.texture_transfer = null_texture_transfer,
|
||||
.framebuffer_create = null_framebuffer_create,
|
||||
.framebuffer_get_debug_label = null_framebuffer_get_debug_label,
|
||||
.framebuffer_set_debug_label = null_framebuffer_set_debug_label,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
static char *material_path(const char *basename);
|
||||
static bool material_check_path(const char *path);
|
||||
static void material_load_stage1(ResourceLoadState *st);
|
||||
static bool material_transfer(void *dst, void *src);
|
||||
|
||||
ResourceHandler material_res_handler = {
|
||||
.type = RES_MATERIAL,
|
||||
|
@ -23,6 +24,7 @@ ResourceHandler material_res_handler = {
|
|||
.find = material_path,
|
||||
.check = material_check_path,
|
||||
.load = material_load_stage1,
|
||||
.transfer = material_transfer,
|
||||
.unload = free,
|
||||
},
|
||||
};
|
||||
|
@ -104,7 +106,8 @@ static void material_load_stage1(ResourceLoadState *st) {
|
|||
|
||||
#define LOADMAP(_map_) do { \
|
||||
if(ld->_map_##_map) { \
|
||||
ld->mat->_map_##_map = get_resource_data(RES_TEXTURE, ld->_map_##_map, st->flags); \
|
||||
ld->mat->_map_##_map = get_resource_data( \
|
||||
RES_TEXTURE, ld->_map_##_map, st->flags & ~RESF_RELOAD); \
|
||||
if(UNLIKELY(ld->mat->_map_##_map == NULL)) { \
|
||||
log_error("%s: failed to load " #_map_ " map '%s'", st->name, ld->_map_##_map); \
|
||||
free_mat_load_data(ld); \
|
||||
|
@ -127,3 +130,11 @@ static void material_load_stage2(ResourceLoadState *st) {
|
|||
res_load_finished(st, ld->mat);
|
||||
free_mat_load_data(ld);
|
||||
}
|
||||
|
||||
static bool material_transfer(void *dst, void *src) {
|
||||
PBRMaterial *mdst = dst;
|
||||
PBRMaterial *msrc = src;
|
||||
*mdst = *msrc;
|
||||
free(msrc);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,11 @@ struct InternalResource {
|
|||
SDL_mutex *mutex;
|
||||
SDL_cond *cond;
|
||||
InternalResLoadState *load;
|
||||
char *name;
|
||||
ResourceStatus status;
|
||||
|
||||
bool is_transient_reloader;
|
||||
InternalResource *reload_buddy;
|
||||
};
|
||||
|
||||
struct InternalResLoadState {
|
||||
|
@ -89,14 +93,72 @@ static struct {
|
|||
uchar no_unload : 1;
|
||||
uchar preload_required : 1;
|
||||
} env;
|
||||
SDL_SpinLock ires_freelist_lock;
|
||||
InternalResource *ires_freelist;
|
||||
} res_gstate;
|
||||
|
||||
INLINE ResourceHandler *get_handler(ResourceType type) {
|
||||
return _handlers[type];
|
||||
}
|
||||
|
||||
INLINE ResourceHandler *get_ires_handler(InternalResource *ires) {
|
||||
return get_handler(ires->res.type);
|
||||
}
|
||||
|
||||
static inline InternalResLoadState *loadstate_internal(ResourceLoadState *st) {
|
||||
return UNION_CAST(ResourceLoadState*, InternalResLoadState*, st);
|
||||
}
|
||||
|
||||
static InternalResource *preload_resource_internal(ResourceType type, const char *name, ResourceFlags flags);
|
||||
|
||||
attr_returns_nonnull
|
||||
static InternalResource *ires_alloc(ResourceType rtype) {
|
||||
SDL_AtomicLock(&res_gstate.ires_freelist_lock);
|
||||
InternalResource *ires = res_gstate.ires_freelist;
|
||||
if(ires) {
|
||||
res_gstate.ires_freelist = ires->reload_buddy;
|
||||
}
|
||||
SDL_AtomicUnlock(&res_gstate.ires_freelist_lock);
|
||||
|
||||
if(!ires) {
|
||||
ires = calloc(1, sizeof(*ires));
|
||||
ires->mutex = SDL_CreateMutex();
|
||||
ires->cond = SDL_CreateCond();
|
||||
}
|
||||
|
||||
ires->res.type = rtype;
|
||||
return ires;
|
||||
}
|
||||
|
||||
attr_nonnull_all
|
||||
static void ires_release(InternalResource *ires) {
|
||||
*ires = (InternalResource) {
|
||||
.mutex = ires->mutex,
|
||||
.cond = ires->cond,
|
||||
};
|
||||
|
||||
SDL_AtomicLock(&res_gstate.ires_freelist_lock);
|
||||
ires->reload_buddy = res_gstate.ires_freelist;
|
||||
res_gstate.ires_freelist = ires;
|
||||
SDL_AtomicUnlock(&res_gstate.ires_freelist_lock);
|
||||
}
|
||||
|
||||
attr_nonnull_all
|
||||
static void ires_free(InternalResource *ires) {
|
||||
SDL_DestroyMutex(ires->mutex);
|
||||
SDL_DestroyCond(ires->cond);
|
||||
free(ires);
|
||||
}
|
||||
|
||||
attr_nonnull_all attr_returns_nonnull
|
||||
static InternalResource *ires_get_persistent(InternalResource *ires) {
|
||||
if(ires->is_transient_reloader) {
|
||||
return NOT_NULL(ires->reload_buddy);
|
||||
}
|
||||
|
||||
return ires;
|
||||
}
|
||||
|
||||
void res_load_failed(ResourceLoadState *st) {
|
||||
InternalResLoadState *ist = loadstate_internal(st);
|
||||
ist->status = LOAD_FAILED;
|
||||
|
@ -131,20 +193,14 @@ void res_load_dependency(ResourceLoadState *st, ResourceType type, const char *n
|
|||
SDL_UnlockMutex(ires->mutex);
|
||||
}
|
||||
|
||||
static inline ResourceHandler *get_handler(ResourceType type) {
|
||||
return *(_handlers + type);
|
||||
}
|
||||
|
||||
static inline ResourceHandler *get_ires_handler(InternalResource *ires) {
|
||||
return get_handler(ires->res.type);
|
||||
}
|
||||
|
||||
static void alloc_handler(ResourceHandler *h) {
|
||||
INLINE void alloc_handler(ResourceHandler *h) {
|
||||
assert(h != NULL);
|
||||
ht_create(&h->private.mapping);
|
||||
}
|
||||
|
||||
static const char *type_name(ResourceType type) {
|
||||
INLINE const char *type_name(ResourceType type) {
|
||||
return get_handler(type)->typename;
|
||||
}
|
||||
|
||||
|
@ -154,14 +210,7 @@ struct valfunc_arg {
|
|||
|
||||
static void *valfunc_begin_load_resource(void* arg) {
|
||||
ResourceType type = ((struct valfunc_arg*)arg)->type;
|
||||
|
||||
InternalResource *ires = calloc(1, sizeof(InternalResource));
|
||||
ires->res.type = type;
|
||||
ires->status = RES_STATUS_LOADING;
|
||||
ires->mutex = SDL_CreateMutex();
|
||||
ires->cond = SDL_CreateCond();
|
||||
|
||||
return ires;
|
||||
return ires_alloc(type);
|
||||
}
|
||||
|
||||
static bool try_begin_load_resource(ResourceType type, const char *name, hash_t hash, InternalResource **out_ires) {
|
||||
|
@ -193,7 +242,7 @@ static ResourceStatus pump_or_wait_for_resource_load(InternalResource *ires, uin
|
|||
Task *task = load_state->async_task;
|
||||
|
||||
if(task) {
|
||||
// If there's an async load task for this resource, wait for it for complete.
|
||||
// If there's an async load task for this resource, wait for it to complete.
|
||||
// If it's not yet running, it will be offloaded to this thread instead.
|
||||
|
||||
load_state->async_task = NULL;
|
||||
|
@ -213,15 +262,23 @@ static ResourceStatus pump_or_wait_for_resource_load(InternalResource *ires, uin
|
|||
if(load_state) {
|
||||
ResourceStatus dep_status = pump_dependencies(load_state);
|
||||
|
||||
if(load_state->ready_to_finalize && is_main_thread()) {
|
||||
// Resource has finished async load, but needs to be finalized on the main thread.
|
||||
// Since we are the main thread, we can do it here.
|
||||
|
||||
if(!pump_only && is_main_thread()) {
|
||||
if(dep_status == RES_STATUS_LOADING) {
|
||||
dep_status = wait_for_dependencies(load_state);
|
||||
wait_for_dependencies(load_state);
|
||||
}
|
||||
|
||||
while(!load_state->ready_to_finalize) {
|
||||
SDL_CondWait(ires->cond, ires->mutex);
|
||||
}
|
||||
|
||||
// May have been finalized while we were sleeping
|
||||
// Can happen if load fails
|
||||
load_state = ires->load;
|
||||
|
||||
if(load_state) {
|
||||
load_resource_finish(load_state);
|
||||
}
|
||||
|
||||
load_resource_finish(load_state);
|
||||
assert(ires->status != RES_STATUS_LOADING);
|
||||
}
|
||||
}
|
||||
|
@ -237,13 +294,19 @@ static ResourceStatus pump_or_wait_for_resource_load(InternalResource *ires, uin
|
|||
ResourceStatus status = ires->status;
|
||||
|
||||
if(status == RES_STATUS_LOADED) {
|
||||
uint32_t missing_flags = want_flags & ~ires->res.flags;
|
||||
uint32_t missing_flags = (want_flags & ~ires->res.flags) & RESF_PROMOTABLE_FLAGS;
|
||||
|
||||
if(missing_flags) {
|
||||
uint32_t new_flags = ires->res.flags | want_flags;
|
||||
log_debug("Flags for %s at %p promoted from 0x%08x to 0x%08x", type_name(ires->res.type), (void*)ires, ires->res.flags, new_flags);
|
||||
ires->res.flags = new_flags;
|
||||
}
|
||||
|
||||
if((want_flags & RESF_RELOAD) && ires->reload_buddy && !ires->is_transient_reloader) {
|
||||
InternalResource *r = ires->reload_buddy;
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
return pump_or_wait_for_resource_load(r, want_flags & ~RESF_RELOAD, pump_only);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
|
@ -284,16 +347,22 @@ static ResourceStatus pump_or_wait_for_dependencies(InternalResLoadState *st, bo
|
|||
return dep_status;
|
||||
}
|
||||
|
||||
#define PROTECT_FLAGS(ist, ...) do { \
|
||||
attr_unused InternalResLoadState *_ist = (ist); \
|
||||
attr_unused ResourceFlags _orig_flags = _ist->st.flags; \
|
||||
{ __VA_ARGS__; } \
|
||||
assert(_ist->st.flags == _orig_flags); \
|
||||
} while(0)
|
||||
|
||||
static void *load_resource_async_task(void *vdata) {
|
||||
InternalResLoadState *st = vdata;
|
||||
InternalResource *ires = st->ires;
|
||||
assume(st == ires->load);
|
||||
|
||||
SDL_LockMutex(ires->mutex);
|
||||
ResourceHandler *h = get_ires_handler(ires);
|
||||
|
||||
st->status = LOAD_NONE;
|
||||
h->procs.load(&st->st);
|
||||
PROTECT_FLAGS(st, h->procs.load(&st->st));
|
||||
|
||||
retry:
|
||||
switch(st->status) {
|
||||
|
@ -308,7 +377,7 @@ retry:
|
|||
}
|
||||
|
||||
st->status = LOAD_NONE;
|
||||
st->continuation(&st->st);
|
||||
PROTECT_FLAGS(st, st->continuation(&st->st));
|
||||
goto retry;
|
||||
} else {
|
||||
dep_status = pump_dependencies(st);
|
||||
|
@ -316,11 +385,12 @@ retry:
|
|||
if(dep_status == RES_STATUS_LOADING) {
|
||||
st->status = LOAD_CONT_ON_MAIN;
|
||||
st->ready_to_finalize = true;
|
||||
SDL_CondBroadcast(ires->cond);
|
||||
events_emit(TE_RESOURCE_ASYNC_LOADED, 0, ires, NULL);
|
||||
break;
|
||||
} else {
|
||||
st->status = LOAD_NONE;
|
||||
st->continuation(&st->st);
|
||||
PROTECT_FLAGS(st, st->continuation(&st->st));
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
@ -331,14 +401,17 @@ retry:
|
|||
case LOAD_CONT_ON_MAIN:
|
||||
if(pump_dependencies(st) == RES_STATUS_LOADING || !is_main_thread()) {
|
||||
st->ready_to_finalize = true;
|
||||
SDL_CondBroadcast(ires->cond);
|
||||
events_emit(TE_RESOURCE_ASYNC_LOADED, 0, ires, NULL);
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
case LOAD_OK:
|
||||
case LOAD_FAILED:
|
||||
SDL_LockMutex(ires->mutex);
|
||||
st->ready_to_finalize = true;
|
||||
load_resource_finish(st);
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
st = NULL;
|
||||
break;
|
||||
|
||||
|
@ -347,7 +420,6 @@ retry:
|
|||
}
|
||||
|
||||
assume(ires->load == st);
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
return st;
|
||||
}
|
||||
|
||||
|
@ -365,9 +437,8 @@ static void unload_resource(InternalResource *ires) {
|
|||
SDL_PumpEvents();
|
||||
SDL_FilterEvents(filter_asyncload_event, ires);
|
||||
|
||||
SDL_DestroyCond(ires->cond);
|
||||
SDL_DestroyMutex(ires->mutex);
|
||||
free(ires);
|
||||
free(ires->name);
|
||||
ires_release(ires);
|
||||
}
|
||||
|
||||
static char *get_name_from_path(ResourceHandler *handler, const char *path) {
|
||||
|
@ -378,7 +449,11 @@ static char *get_name_from_path(ResourceHandler *handler, const char *path) {
|
|||
return resource_util_basename(handler->subdir, path);
|
||||
}
|
||||
|
||||
static bool should_defer_load(void) {
|
||||
static bool should_defer_load(InternalResLoadState *st) {
|
||||
if(st->st.flags & RESF_RELOAD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FrameTimes ft = eventloop_get_frame_times();
|
||||
|
||||
if(ft.next != res_gstate.frame_threshold) {
|
||||
|
@ -409,19 +484,20 @@ static bool resource_asyncload_handler(SDL_Event *evt, void *arg) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#if 1
|
||||
if(should_defer_load()) {
|
||||
if(should_defer_load(st)) {
|
||||
events_defer(evt);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_LockMutex(ires->mutex);
|
||||
|
||||
ResourceStatus dep_status = pump_dependencies(st);
|
||||
|
||||
if(dep_status == RES_STATUS_LOADING) {
|
||||
log_debug("Deferring %s '%s' because some dependencies are not satisfied", type_name(ires->res.type), st->st.name);
|
||||
// log_debug("Deferring %s '%s' because some dependencies are not satisfied", type_name(ires->res.type), st->st.name);
|
||||
|
||||
// FIXME: Make this less braindead.
|
||||
// This will retry every frame until dependencies are satisfied.
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
events_defer(evt);
|
||||
return true;
|
||||
|
@ -492,6 +568,13 @@ static void load_resource(InternalResource *ires, const char *name, ResourceFlag
|
|||
flags |= RESF_OPTIONAL;
|
||||
}
|
||||
|
||||
if(ires->name == NULL) {
|
||||
ires->name = strdup(name);
|
||||
} else {
|
||||
name = ires->name;
|
||||
assume(flags & RESF_RELOAD);
|
||||
}
|
||||
|
||||
path = handler->procs.find(name);
|
||||
|
||||
if(path) {
|
||||
|
@ -523,7 +606,7 @@ static void load_resource(InternalResource *ires, const char *name, ResourceFlag
|
|||
load_resource_async(&st);
|
||||
} else {
|
||||
st.status = LOAD_NONE;
|
||||
handler->procs.load(&st.st);
|
||||
PROTECT_FLAGS(&st, handler->procs.load(&st.st));
|
||||
|
||||
retry: switch(st.status) {
|
||||
case LOAD_OK:
|
||||
|
@ -535,13 +618,55 @@ static void load_resource(InternalResource *ires, const char *name, ResourceFlag
|
|||
case LOAD_CONT_ON_MAIN:
|
||||
wait_for_dependencies(&st);
|
||||
st.status = LOAD_NONE;
|
||||
st.continuation(&st.st);
|
||||
PROTECT_FLAGS(&st, st.continuation(&st.st));
|
||||
goto retry;
|
||||
default: UNREACHABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool reload_resource(InternalResource *ires, ResourceFlags flags, bool async) {
|
||||
ResourceHandler *handler = get_ires_handler(ires);
|
||||
const char *typename = type_name(handler->type);
|
||||
|
||||
if(!handler->procs.transfer) {
|
||||
log_debug("Can't reload %s '%s'", typename, ires->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
flags |= RESF_RELOAD | RESF_OPTIONAL;
|
||||
|
||||
SDL_LockMutex(ires->mutex);
|
||||
assert(!ires->is_transient_reloader);
|
||||
|
||||
if(ires->status != RES_STATUS_LOADED) {
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
log_warn("Tried to reload %s '%s' that is not loaded", typename, ires->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ires->reload_buddy) {
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
log_warn("Tried to reload %s '%s' that is currently reloading", typename, ires->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Reloading %s '%s'", typename, ires->name);
|
||||
|
||||
InternalResource *transient = ires_alloc(ires->res.type);
|
||||
transient->name = ires->name;
|
||||
transient->status = RES_STATUS_LOADING;
|
||||
transient->reload_buddy = ires;
|
||||
transient->is_transient_reloader = true;
|
||||
ires->reload_buddy = transient;
|
||||
|
||||
load_resource(transient, ires->name, flags, async);
|
||||
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void load_resource_finish(InternalResLoadState *st) {
|
||||
void *raw = NULL;
|
||||
InternalResource *ires = st->ires;
|
||||
|
@ -615,6 +740,34 @@ static void load_resource_finish(InternalResLoadState *st) {
|
|||
|
||||
SDL_CondBroadcast(ires->cond);
|
||||
assert(ires->status != RES_STATUS_LOADING);
|
||||
|
||||
// handle reload
|
||||
|
||||
InternalResource *persistent = ires_get_persistent(ires);
|
||||
if(persistent == ires) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceHandler *handler = get_ires_handler(ires);
|
||||
assert(handler->procs.transfer != NULL);
|
||||
|
||||
SDL_LockMutex(persistent->mutex);
|
||||
|
||||
if(
|
||||
!ires->res.data ||
|
||||
!handler->procs.transfer(NOT_NULL(persistent->res.data), ires->res.data)
|
||||
) {
|
||||
log_error("Failed to reload %s '%s'", typename, persistent->name);
|
||||
}
|
||||
|
||||
persistent->reload_buddy = NULL;
|
||||
persistent->res.flags |= ires->res.flags & ~(RESF_RELOAD | RESF_OPTIONAL);
|
||||
assert(persistent->status == RES_STATUS_LOADED);
|
||||
|
||||
SDL_CondBroadcast(persistent->cond);
|
||||
SDL_UnlockMutex(persistent->mutex);
|
||||
|
||||
ires_release(ires);
|
||||
}
|
||||
|
||||
Resource *_get_resource(ResourceType type, const char *name, hash_t hash, ResourceFlags flags) {
|
||||
|
@ -622,6 +775,8 @@ Resource *_get_resource(ResourceType type, const char *name, hash_t hash, Resour
|
|||
Resource *res;
|
||||
|
||||
if(try_begin_load_resource(type, name, hash, &ires)) {
|
||||
flags &= ~RESF_RELOAD;
|
||||
|
||||
SDL_LockMutex(ires->mutex);
|
||||
|
||||
if(!(flags & RESF_PRELOAD)) {
|
||||
|
@ -646,8 +801,11 @@ Resource *_get_resource(ResourceType type, const char *name, hash_t hash, Resour
|
|||
SDL_UnlockMutex(ires->mutex);
|
||||
return res;
|
||||
} else {
|
||||
uint32_t promotion_flags = flags & RESF_PERMANENT;
|
||||
ResourceStatus status = wait_for_resource_load(ires, promotion_flags);
|
||||
if(flags & RESF_RELOAD) {
|
||||
reload_resource(ires, flags, false);
|
||||
}
|
||||
|
||||
ResourceStatus status = wait_for_resource_load(ires, flags);
|
||||
|
||||
if(status == RES_STATUS_FAILED) {
|
||||
return NULL;
|
||||
|
@ -677,6 +835,8 @@ static InternalResource *preload_resource_internal(ResourceType type, const char
|
|||
SDL_LockMutex(ires->mutex);
|
||||
load_resource(ires, name, flags, !res_gstate.env.no_async_load);
|
||||
SDL_UnlockMutex(ires->mutex);
|
||||
} else if(flags & RESF_RELOAD) {
|
||||
reload_resource(ires, flags, !res_gstate.env.no_async_load);
|
||||
}
|
||||
|
||||
return ires;
|
||||
|
@ -699,6 +859,29 @@ void preload_resources(ResourceType type, ResourceFlags flags, const char *first
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
static void reload_resources(ResourceHandler *h) {
|
||||
if(!h->procs.transfer) {
|
||||
return;
|
||||
}
|
||||
|
||||
ht_str2ptr_ts_iter_t iter;
|
||||
ht_iter_begin(&h->private.mapping, &iter);
|
||||
|
||||
for(; iter.has_data; ht_iter_next(&iter)) {
|
||||
reload_resource(iter.value, 0, !res_gstate.env.no_async_load);
|
||||
}
|
||||
|
||||
ht_iter_end(&iter);
|
||||
}
|
||||
|
||||
void reload_all_resources(void) {
|
||||
for(uint i = 0; i < RES_NUMTYPES; ++i) {
|
||||
ResourceHandler *h = get_handler(i);
|
||||
assert(h != NULL);
|
||||
reload_resources(h);
|
||||
}
|
||||
}
|
||||
|
||||
void init_resources(void) {
|
||||
res_gstate.env.no_async_load = env_get("TAISEI_NOASYNC", false);
|
||||
res_gstate.env.no_preload = env_get("TAISEI_NOPRELOAD", false);
|
||||
|
@ -897,6 +1080,14 @@ void free_resources(bool all) {
|
|||
return;
|
||||
}
|
||||
|
||||
for(InternalResource *ires = res_gstate.ires_freelist; ires;) {
|
||||
InternalResource *next = ires->reload_buddy;
|
||||
ires_free(ires);
|
||||
ires = next;
|
||||
}
|
||||
|
||||
res_gstate.ires_freelist = NULL;
|
||||
|
||||
if(!res_gstate.env.no_async_load) {
|
||||
events_unregister_handler(resource_asyncload_handler);
|
||||
}
|
||||
|
|
|
@ -28,10 +28,12 @@ typedef enum ResourceType {
|
|||
|
||||
typedef enum ResourceFlags {
|
||||
RESF_OPTIONAL = 1,
|
||||
RESF_PERMANENT = 2,
|
||||
RESF_PERMANENT = 2, // TODO get rid of this cancer
|
||||
RESF_PRELOAD = 4,
|
||||
RESF_RELOAD = 8,
|
||||
|
||||
RESF_DEFAULT = 0,
|
||||
RESF_PROMOTABLE_FLAGS = RESF_PERMANENT, // TODO get rid of this cancer
|
||||
} ResourceFlags;
|
||||
|
||||
typedef struct ResourceLoadState ResourceLoadState;
|
||||
|
@ -47,7 +49,7 @@ struct ResourceLoadState {
|
|||
// The path may not actually exist or be usable. The load function (see below) shall deal with such cases.
|
||||
// The returned path must be free()'d.
|
||||
// May return NULL on failure, but does not have to.
|
||||
typedef char* (*ResourceFindProc)(const char *name);
|
||||
typedef char *(*ResourceFindProc)(const char *name);
|
||||
|
||||
// Tells whether the resource handler should attempt to load a file, specified by a vfs path.
|
||||
typedef bool (*ResourceCheckProc)(const char *path);
|
||||
|
@ -56,6 +58,15 @@ typedef bool (*ResourceCheckProc)(const char *path);
|
|||
// Must call one of the following res_load_* functions before returning to indicate status.
|
||||
typedef void (*ResourceLoadProc)(ResourceLoadState *st);
|
||||
|
||||
// Makes `dst` refer to the resource represented by `src`.
|
||||
// `src` may no longer be a valid reference to the resource after this operation.
|
||||
// Resource previously represented by `dst` is destroyed in the process.
|
||||
// Called on the main thread, after a resource reload successfully completes.
|
||||
//
|
||||
// Returns true if the operation succeeds.
|
||||
// In case of failure, `dst` must not be modified, and `src` must be destroyed.
|
||||
typedef bool (*ResourceTransferProc)(void *dst, void *src);
|
||||
|
||||
void res_load_failed(ResourceLoadState *st) attr_nonnull(1);
|
||||
void res_load_finished(ResourceLoadState *st, void *res) attr_nonnull(1, 2);
|
||||
|
||||
|
@ -96,6 +107,7 @@ typedef struct ResourceHandler {
|
|||
ResourceCheckProc check;
|
||||
ResourceLoadProc load;
|
||||
ResourceUnloadProc unload;
|
||||
ResourceTransferProc transfer;
|
||||
ResourceInitProc init;
|
||||
ResourcePostInitProc post_init;
|
||||
ResourceShutdownProc shutdown;
|
||||
|
@ -115,6 +127,7 @@ typedef struct Resource {
|
|||
void init_resources(void);
|
||||
void load_resources(void);
|
||||
void free_resources(bool all);
|
||||
void reload_all_resources(void);
|
||||
|
||||
Resource *_get_resource(ResourceType type, const char *name, hash_t hash, ResourceFlags flags) attr_nonnull_all;
|
||||
void *_get_resource_data(ResourceType type, const char *name, hash_t hash, ResourceFlags flags) attr_nonnull_all;
|
||||
|
|
|
@ -33,7 +33,7 @@ static struct shobj_type shobj_type_table[] = {
|
|||
{ NULL }
|
||||
};
|
||||
|
||||
static struct shobj_type* get_shobj_type(const char *name) {
|
||||
static struct shobj_type *get_shobj_type(const char *name) {
|
||||
for(struct shobj_type *type = shobj_type_table; type->ext; ++type) {
|
||||
if(strendswith(name, type->ext)) {
|
||||
return type;
|
||||
|
@ -43,7 +43,7 @@ static struct shobj_type* get_shobj_type(const char *name) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static char* shader_object_path(const char *name) {
|
||||
static char *shader_object_path(const char *name) {
|
||||
char *path = NULL;
|
||||
|
||||
for(const char *const *ext = shobj_exts; *ext; ++ext) {
|
||||
|
@ -163,6 +163,10 @@ static void unload_shader_object(void *vsha) {
|
|||
r_shader_object_destroy(vsha);
|
||||
}
|
||||
|
||||
static bool transfer_shader_object(void *dst, void *src) {
|
||||
return r_shader_object_transfer(dst, src);
|
||||
}
|
||||
|
||||
ResourceHandler shader_object_res_handler = {
|
||||
.type = RES_SHADER_OBJECT,
|
||||
.typename = "shader object",
|
||||
|
@ -175,5 +179,6 @@ ResourceHandler shader_object_res_handler = {
|
|||
.check = check_shader_object_path,
|
||||
.load = load_shader_object_stage1,
|
||||
.unload = unload_shader_object,
|
||||
.transfer = transfer_shader_object,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -78,7 +78,7 @@ static void load_shader_program_stage2(ResourceLoadState *st) {
|
|||
char *objname = ldata.objlist;
|
||||
|
||||
for(int i = 0; i < ldata.num_objects; ++i) {
|
||||
if(!(objs[i] = get_resource_data(RES_SHADER_OBJECT, objname, st->flags))) {
|
||||
if(!(objs[i] = get_resource_data(RES_SHADER_OBJECT, objname, st->flags & ~RESF_RELOAD))) {
|
||||
log_error("%s: couldn't load shader object '%s'", st->path, objname);
|
||||
free(ldata.objlist);
|
||||
res_load_failed(st);
|
||||
|
@ -104,6 +104,10 @@ static void unload_shader_program(void *vprog) {
|
|||
r_shader_program_destroy(vprog);
|
||||
}
|
||||
|
||||
static bool transfer_shader_program(void *dst, void *src) {
|
||||
return r_shader_program_transfer(dst, src);
|
||||
}
|
||||
|
||||
ResourceHandler shader_program_res_handler = {
|
||||
.type = RES_SHADER_PROGRAM,
|
||||
.typename = "shader program",
|
||||
|
@ -114,5 +118,6 @@ ResourceHandler shader_program_res_handler = {
|
|||
.check = check_shader_program_path,
|
||||
.load = load_shader_program_stage1,
|
||||
.unload = unload_shader_program,
|
||||
.transfer = transfer_shader_program,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -45,7 +45,6 @@ static void load_sprite_stage1(ResourceLoadState *st) {
|
|||
|
||||
if(texture_res_handler.procs.check(st->path)) {
|
||||
state->texture_name = strdup(st->name);
|
||||
// preload_resource(RES_TEXTURE, state->texture_name, st->flags);
|
||||
res_load_dependency(st, RES_TEXTURE, state->texture_name);
|
||||
res_load_continue_after_dependencies(st, load_sprite_stage2, state);
|
||||
return;
|
||||
|
@ -97,7 +96,7 @@ static void load_sprite_stage2(ResourceLoadState *st) {
|
|||
struct sprite_load_state *state = NOT_NULL(st->opaque);
|
||||
Sprite *spr = NOT_NULL(state->spr);
|
||||
|
||||
spr->tex = get_resource_data(RES_TEXTURE, state->texture_name, st->flags);
|
||||
spr->tex = get_resource_data(RES_TEXTURE, state->texture_name, st->flags & ~RESF_RELOAD);
|
||||
|
||||
free(state->texture_name);
|
||||
free(state);
|
||||
|
@ -200,6 +199,12 @@ void sprite_set_denormalized_tex_coords(Sprite *restrict spr, FloatRect tc) {
|
|||
spr->tex_area.h = tc.h / tex_h;
|
||||
}
|
||||
|
||||
static bool transfer_sprite(void *dst, void *src) {
|
||||
*(Sprite*)dst = *(Sprite*)src;
|
||||
free(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceHandler sprite_res_handler = {
|
||||
.type = RES_SPRITE,
|
||||
.typename = "sprite",
|
||||
|
@ -210,5 +215,6 @@ ResourceHandler sprite_res_handler = {
|
|||
.check = check_sprite_path,
|
||||
.load = load_sprite_stage1,
|
||||
.unload = free,
|
||||
.transfer = transfer_sprite,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
|
||||
#include "global.h"
|
||||
#include "video.h"
|
||||
#include "renderer/api.h"
|
||||
|
||||
static bool texture_transfer(void *dst, void *src) {
|
||||
return r_texture_transfer(dst, src);
|
||||
}
|
||||
|
||||
ResourceHandler texture_res_handler = {
|
||||
.type = RES_TEXTURE,
|
||||
|
@ -23,6 +28,7 @@ ResourceHandler texture_res_handler = {
|
|||
.check = texture_loader_check_path,
|
||||
.load = texture_loader_stage1,
|
||||
.unload = texture_loader_unload,
|
||||
.transfer = texture_transfer,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue