diff --git a/src/rendering/twn_draw_c.h b/src/rendering/twn_draw_c.h index 9125d7b..d618c01 100644 --- a/src/rendering/twn_draw_c.h +++ b/src/rendering/twn_draw_c.h @@ -341,5 +341,7 @@ void issue_deferred_draw_commands(void); bool model_load_workers_thread(void); void finally_draw_models(void); +void free_model_cache(void); +void model_state_deinit(void); #endif diff --git a/src/rendering/twn_model.c b/src/rendering/twn_model.c index 101f054..4d3e61b 100644 --- a/src/rendering/twn_model.c +++ b/src/rendering/twn_model.c @@ -1,6 +1,5 @@ #include "twn_draw_c.h" #include "twn_draw.h" -#include "twn_util.h" #include "twn_workers_c.h" #define FAST_OBJ_IMPLEMENTATION @@ -13,7 +12,7 @@ static struct ModelCacheItem { - char const *key; + char *key; struct ModelCacheItemValue { fastObjMesh *mesh; } value; @@ -29,7 +28,7 @@ static struct ModelDrawCommand { /* deferred queue of model files to load from worker threads */ static SDL_mutex *model_load_mutex; -static char **model_load_queue; +static char const **model_load_queue; static bool model_load_initialized; /* use streaming via callbacks to reduce memory congestion */ @@ -69,7 +68,7 @@ static bool model_load_workers_finished(void) { /* returns false if there was nothing to do */ bool model_load_workers_thread(void) { /* attempt to grab something to work on */ - char *load_request = NULL; + char const *load_request = NULL; SDL_LockMutex(model_load_mutex); if (arrlenu(model_load_queue) != 0) load_request = arrpop(model_load_queue); @@ -107,26 +106,26 @@ void draw_model(const char *model, model_load_initialized = true; } - /* make sure not to reference parameter longer than duration of this function */ - char *model_copy = SDL_strdup(model); - model = NULL; /* trap */ + struct ModelCacheItem const *item; + + /* if model is missing, queue it up for loading */ + SDL_LockMutex(model_load_mutex); + if (!(item = shgetp_null(model_cache, model))) { + model = SDL_strdup(model); + shput(model_cache, model, (struct ModelCacheItemValue){0}); + arrpush(model_load_queue, model); + SDL_SemPost(workers_job_semaphore); + } else + model = item->key; + SDL_UnlockMutex(model_load_mutex); struct ModelDrawCommand const command = { - .model = model_copy, + .model = (char *)model, .position = position, .rotation = rotation, .scale = scale }; arrpush(model_draw_commands, command); - - /* if model is missing, queue it up for loading */ - SDL_LockMutex(model_load_mutex); - if (!(shgetp_null(model_cache, model_copy))) { - shput(model_cache, model_copy, (struct ModelCacheItemValue){0}); - arrpush(model_load_queue, model_copy); - SDL_SemPost(workers_job_semaphore); - } - SDL_UnlockMutex(model_load_mutex); } @@ -147,20 +146,57 @@ void finally_draw_models(void) { // fastObjTexture const *const texture = &mesh->textures[group->face_offset + f]; // log_info("material: %s", material->name); /* TODO: support arbitrary fans */ - SDL_assert(vertices == 4); - fastObjIndex i0 = mesh->indices[group->index_offset + idx + 0]; - fastObjIndex i1 = mesh->indices[group->index_offset + idx + 1]; - fastObjIndex i2 = mesh->indices[group->index_offset + idx + 2]; - fastObjIndex i3 = mesh->indices[group->index_offset + idx + 3]; - draw_quad( - "asd", - (Vec3) { mesh->positions[3 * i0.p + 0], mesh->positions[3 * i0.p + 1], mesh->positions[3 * i0.p + 2] }, - (Vec3) { mesh->positions[3 * i1.p + 0], mesh->positions[3 * i1.p + 1], mesh->positions[3 * i1.p + 2] }, - (Vec3) { mesh->positions[3 * i2.p + 0], mesh->positions[3 * i2.p + 1], mesh->positions[3 * i2.p + 2] }, - (Vec3) { mesh->positions[3 * i3.p + 0], mesh->positions[3 * i3.p + 1], mesh->positions[3 * i3.p + 2] }, - (Rect) { .w = 64, .h = 64 }, - (Color) { 255, 255, 255, 255 } - ); + unsigned int const material_index = mesh->face_materials[group->index_offset + f]; + fastObjMaterial const *const material = mesh->materials ? &mesh->materials[material_index] : NULL; + if (vertices == 4) { + fastObjIndex const i0 = mesh->indices[group->index_offset + idx + 0]; + fastObjIndex const i1 = mesh->indices[group->index_offset + idx + 1]; + fastObjIndex const i2 = mesh->indices[group->index_offset + idx + 2]; + fastObjIndex const i3 = mesh->indices[group->index_offset + idx + 3]; + draw_quad( + material ? mesh->textures[material->map_Kd].name : NULL, + (Vec3) { mesh->positions[3 * i0.p + 0] * command->scale.x, mesh->positions[3 * i0.p + 1] * command->scale.y, mesh->positions[3 * i0.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i1.p + 0] * command->scale.x, mesh->positions[3 * i1.p + 1] * command->scale.y, mesh->positions[3 * i1.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i2.p + 0] * command->scale.x, mesh->positions[3 * i2.p + 1] * command->scale.y, mesh->positions[3 * i2.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i3.p + 0] * command->scale.x, mesh->positions[3 * i3.p + 1] * command->scale.y, mesh->positions[3 * i3.p + 2] * command->scale.z }, + (Rect) { .w = 64, .h = 64 }, + (Color) { 255, 255, 255, 255 } + ); + } else if (vertices == 3) { + fastObjIndex const i0 = mesh->indices[group->index_offset + idx + 0]; + fastObjIndex const i1 = mesh->indices[group->index_offset + idx + 1]; + fastObjIndex const i2 = mesh->indices[group->index_offset + idx + 2]; + draw_triangle( + material ? mesh->textures[material->map_Kd].name : NULL, + (Vec3) { mesh->positions[3 * i0.p + 0] * command->scale.x, mesh->positions[3 * i0.p + 1] * command->scale.y, mesh->positions[3 * i0.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i1.p + 0] * command->scale.x, mesh->positions[3 * i1.p + 1] * command->scale.y, mesh->positions[3 * i1.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i2.p + 0] * command->scale.x, mesh->positions[3 * i2.p + 1] * command->scale.y, mesh->positions[3 * i2.p + 2] * command->scale.z }, + (Vec2) {0,0}, + (Vec2) {0,0}, + (Vec2) {0,0}, + (Color){255, 255, 255, 255}, + (Color){255, 255, 255, 255}, + (Color){255, 255, 255, 255} + ); + } else { + fastObjIndex const i0 = mesh->indices[group->index_offset + idx]; + for (unsigned int z = 0; z < vertices - 2; ++z) { + fastObjIndex const i1 = mesh->indices[group->index_offset + idx + 1 + z]; + fastObjIndex const i2 = mesh->indices[group->index_offset + idx + 2 + z]; + draw_triangle( + material ? mesh->textures[material->map_Kd].name : NULL, + (Vec3) { mesh->positions[3 * i0.p + 0] * command->scale.x, mesh->positions[3 * i0.p + 1] * command->scale.y, mesh->positions[3 * i0.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i1.p + 0] * command->scale.x, mesh->positions[3 * i1.p + 1] * command->scale.y, mesh->positions[3 * i1.p + 2] * command->scale.z }, + (Vec3) { mesh->positions[3 * i2.p + 0] * command->scale.x, mesh->positions[3 * i2.p + 1] * command->scale.y, mesh->positions[3 * i2.p + 2] * command->scale.z }, + (Vec2) {0,0}, + (Vec2) {0,0}, + (Vec2) {0,0}, + (Color){255, 255, 255, 255}, + (Color){255, 255, 255, 255}, + (Color){255, 255, 255, 255} + ); + } + } idx += vertices; } } @@ -168,3 +204,27 @@ void finally_draw_models(void) { arrsetlen(model_draw_commands, 0); } + + +/* drop model caches */ +void free_model_cache(void) { + while (!model_load_workers_finished()) { + (void)0; + } + + for (size_t i = 0; i < shlenu(model_cache); ++i) { + fast_obj_destroy(model_cache[i].value.mesh); + SDL_free(model_cache[i].key); + } + + shfree(model_cache); +} + + +void model_state_deinit(void) { + SDL_assert(model_load_initialized); + free_model_cache(); + arrfree(model_load_queue); + SDL_DestroyMutex(model_load_mutex); + model_load_initialized = false; +} diff --git a/src/twn_loop.c b/src/twn_loop.c index aa1b73a..6c4378e 100644 --- a/src/twn_loop.c +++ b/src/twn_loop.c @@ -726,6 +726,7 @@ static void clean_up(void) { toml_free(ctx.config_table); PHYSFS_deinit(); workers_deinit(); + model_state_deinit(); SDL_free(ctx.base_dir); SDL_free(ctx.title); SDL_GL_DeleteContext(ctx.gl_context); diff --git a/src/twn_workers.c b/src/twn_workers.c index 7dd64cd..e4a1a18 100644 --- a/src/twn_workers.c +++ b/src/twn_workers.c @@ -4,10 +4,10 @@ SDL_sem *workers_job_semaphore; -static SDL_Thread *workers_pool[MAX_WORKERS]; static size_t workers_pool_size; static SDL_mutex *workers_mutex; static bool workers_should_exit; +static SDL_sem *workers_exit_semaphore; /* should come to count of `workers_pool_size` */ /* logic is such that when job is posted, worker threads attempt to grab it from any possible entry point */ /* if it did something, which is signaled by `true` return, go back to waiting on semaphore, so that it's decremented properly */ @@ -31,6 +31,9 @@ static int worker_thread(void *udata) { continue; } + /* let the main thread collect it */ + SDL_SemPost(workers_exit_semaphore); + return 0; } @@ -52,6 +55,7 @@ bool workers_init(size_t worker_count) { workers_pool_size = worker_count; workers_job_semaphore = SDL_CreateSemaphore(0); + workers_exit_semaphore = SDL_CreateSemaphore(0); workers_mutex = SDL_CreateMutex(); return true; } @@ -62,8 +66,12 @@ void workers_deinit(void) { workers_should_exit = true; SDL_UnlockMutex(workers_mutex); - /* TODO: that's not correct */ + for (size_t i = 0; i < workers_pool_size; ++i) { + SDL_SemWait(workers_exit_semaphore); + } + SDL_DestroyMutex(workers_mutex); SDL_DestroySemaphore(workers_job_semaphore); + SDL_DestroySemaphore(workers_exit_semaphore); workers_pool_size = 0; }