wip model loading + workers
This commit is contained in:
@ -393,7 +393,10 @@ static void render_2d(void) {
|
||||
}
|
||||
|
||||
|
||||
/* TODO: benchmark which order works best for expected cases */
|
||||
static void render_space(void) {
|
||||
finally_draw_models();
|
||||
|
||||
/* nothing to do, abort */
|
||||
/* as space pipeline isn't used we can have fewer changes and initialization costs */
|
||||
if (hmlenu(ctx.uncolored_mesh_batches) != 0 || hmlenu(ctx.billboard_batches) != 0) {
|
||||
|
@ -132,7 +132,7 @@ typedef struct MeshBatch {
|
||||
|
||||
/* TODO: use atlas id instead */
|
||||
typedef struct MeshBatchItem {
|
||||
TextureKey key;
|
||||
struct TextureKey key;
|
||||
struct MeshBatch value;
|
||||
} MeshBatchItem;
|
||||
|
||||
@ -339,4 +339,7 @@ void finally_draw_command(DeferredCommandDraw command);
|
||||
|
||||
void issue_deferred_draw_commands(void);
|
||||
|
||||
bool model_load_workers_thread(void);
|
||||
void finally_draw_models(void);
|
||||
|
||||
#endif
|
||||
|
170
src/rendering/twn_model.c
Normal file
170
src/rendering/twn_model.c
Normal file
@ -0,0 +1,170 @@
|
||||
#include "twn_draw_c.h"
|
||||
#include "twn_draw.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_workers_c.h"
|
||||
|
||||
#define FAST_OBJ_IMPLEMENTATION
|
||||
#define FAST_OBJ_REALLOC SDL_realloc
|
||||
#define FAST_OBJ_FREE SDL_free
|
||||
#include <fast_obj.h>
|
||||
#include <stb_ds.h>
|
||||
#include <physfs.h>
|
||||
#include <physfsrwops.h>
|
||||
|
||||
|
||||
static struct ModelCacheItem {
|
||||
char const *key;
|
||||
struct ModelCacheItemValue {
|
||||
fastObjMesh *mesh;
|
||||
} value;
|
||||
} *model_cache;
|
||||
|
||||
/* TODO: store index to model cache instead */
|
||||
static struct ModelDrawCommand {
|
||||
char *model;
|
||||
Vec3 position;
|
||||
Vec3 rotation;
|
||||
Vec3 scale;
|
||||
} *model_draw_commands;
|
||||
|
||||
/* deferred queue of model files to load from worker threads */
|
||||
static SDL_mutex *model_load_mutex;
|
||||
static char **model_load_queue;
|
||||
static bool model_load_initialized;
|
||||
|
||||
/* use streaming via callbacks to reduce memory congestion */
|
||||
static void model_load_callback_close(void *handle, void *udata) {
|
||||
(void)udata;
|
||||
((SDL_RWops *)handle)->close(handle);
|
||||
}
|
||||
|
||||
static void *model_load_callback_open(const char *path, void *udata) {
|
||||
(void)udata;
|
||||
return PHYSFSRWOPS_openRead(path);
|
||||
}
|
||||
|
||||
static size_t model_load_callback_read(void *handle, void *dst, size_t bytes, void *udata) {
|
||||
(void)udata;
|
||||
return ((SDL_RWops *)handle)->read(handle, dst, 1, bytes);
|
||||
}
|
||||
|
||||
static unsigned long model_load_callback_size(void *handle, void *udata) {
|
||||
(void)udata;
|
||||
return ((SDL_RWops *)handle)->size(handle);
|
||||
}
|
||||
|
||||
|
||||
/* TODO: is there a way to do this nicely while locking main thread? */
|
||||
/* sleeping over atomic counter might be good enough i guess */
|
||||
static bool model_load_workers_finished(void) {
|
||||
bool result;
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
result = arrlenu(model_load_queue) == 0;
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* entry point for workers, polled every time a job semaphore is posted */
|
||||
/* 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;
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
if (arrlenu(model_load_queue) != 0)
|
||||
load_request = arrpop(model_load_queue);
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
/* nothing to do, bail */
|
||||
if (!load_request)
|
||||
return false;
|
||||
|
||||
fastObjCallbacks const callbacks = {
|
||||
.file_close = model_load_callback_close,
|
||||
.file_open = model_load_callback_open,
|
||||
.file_read = model_load_callback_read,
|
||||
.file_size = model_load_callback_size
|
||||
};
|
||||
|
||||
/* TODO: immediately create jobs for missing textures */
|
||||
fastObjMesh *mesh = fast_obj_read_with_callbacks(load_request, &callbacks, NULL);
|
||||
|
||||
SDL_LockMutex(model_load_mutex);
|
||||
struct ModelCacheItem *item = shgetp(model_cache, load_request);
|
||||
item->value.mesh = mesh;
|
||||
SDL_UnlockMutex(model_load_mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void draw_model(const char *model,
|
||||
Vec3 position,
|
||||
Vec3 rotation,
|
||||
Vec3 scale)
|
||||
{
|
||||
if (!model_load_initialized) {
|
||||
model_load_mutex = SDL_CreateMutex();
|
||||
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 ModelDrawCommand const command = {
|
||||
.model = model_copy,
|
||||
.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);
|
||||
}
|
||||
|
||||
|
||||
void finally_draw_models(void) {
|
||||
while (!model_load_workers_finished()) {
|
||||
(void)0;
|
||||
}
|
||||
|
||||
/* TODO: have special path for them, preserving the buffers and potentially using instanced draw */
|
||||
for (int i = 0; i < arrlen(model_draw_commands); ++i) {
|
||||
struct ModelDrawCommand const *const command = &model_draw_commands[i];
|
||||
fastObjMesh const *const mesh = model_cache[shgeti(model_cache, command->model)].value.mesh;
|
||||
for (unsigned int g = 0; g < mesh->group_count; ++g) {
|
||||
fastObjGroup const *const group = &mesh->groups[g];
|
||||
unsigned int idx = 0;
|
||||
for (unsigned int f = 0; f < group->face_count; ++f) {
|
||||
unsigned int const vertices = mesh->face_vertices[group->face_offset + f];
|
||||
// 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 }
|
||||
);
|
||||
idx += vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrsetlen(model_draw_commands, 0);
|
||||
}
|
Reference in New Issue
Block a user