sheet loader thread

This commit is contained in:
jacob 2024-04-26 14:00:31 -05:00
parent 925ef5a482
commit 35ded9dbac
7 changed files with 163 additions and 73 deletions

View File

@ -420,7 +420,7 @@ INTERNAL void game_update(void)
f64 time_in_frame = ent->animation_time_in_frame + G.world.dt;
u64 span_frame_offset = ent->animation_frame;
struct sheet *sheet = sheet_from_tag_await(sheet_frame_scope, ent->sprite_sheet_tag);
struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, ent->sprite_sheet_tag);
struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
u64 frame_index = span.start + span_frame_offset;

View File

@ -10,6 +10,7 @@
#include "work.h"
#include "atomic.h"
#include "thread_local.h"
#include "app.h"
#define SHEET_ARENA_RESERVE MEGABYTE(64)
#define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0
@ -18,26 +19,21 @@
#define CACHE_BUCKETS_COUNT 256
#define SCOPE_BUCKETS_COUNT 256
#define MAX_LOADER_THREADS 4
/* ========================== *
* Load cmd structs
* ========================== */
struct load_cmd {
struct cache_node *node;
struct load_cmd *next;
struct load_cmd *next_free;
struct cache_node *cache_node;
struct sheet_tag tag;
u8 tag_path_buff[4096];
};
struct load_cmd_store {
struct arena arena;
struct sys_mutex mutex;
};
struct load_cmd_array {
u64 count;
struct load_cmd *cmds;
};
/* ========================== *
* Cache structs
* ========================== */
@ -83,11 +79,27 @@ struct sheet_scope_reference {
* ========================== */
GLOBAL struct {
struct load_cmd_store load_cmd_store;
/* Cache */
struct cache cache;
/* Load cmds */
b32 shutdown;
struct arena load_cmd_arena;
struct sys_mutex load_cmd_mutex;
struct sys_condition_variable load_cmd_or_shutdown_cv;
struct load_cmd *first_free_load_cmd;
struct load_cmd *first_load_cmd;
struct load_cmd *last_load_cmd;
/* Threads */
u64 loader_threads_count;
struct sys_thread loader_threads[MAX_LOADER_THREADS];
struct sys_thread evictor_thread;
} G = { 0 }, DEBUG_ALIAS(G, G_sheet);
GLOBAL READONLY struct sheet g_sheet_nil = { 0 };
GLOBAL READONLY struct sheet g_sheet_loading = {
.loading = true
};
/* ========================== *
* Thread local state
@ -117,6 +129,9 @@ GLOBAL THREAD_LOCAL_VAR_DEF(tl_sheet_tctx, struct sheet_tctx, sheet_tctx_alloc,
* Startup
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown);
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg);
struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
struct asset_cache_startup_receipt *asset_cache_sr,
struct resource_startup_receipt *resource_sr)
@ -125,16 +140,52 @@ struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
(UNUSED)asset_cache_sr;
(UNUSED)resource_sr;
G.load_cmd_store.arena = arena_alloc(GIGABYTE(64));
G.load_cmd_store.mutex = sys_mutex_alloc();
G.cache.rw_mutex = sys_rw_mutex_alloc();
G.cache.arena = arena_alloc(GIGABYTE(64));
G.cache.buckets = arena_push_array_zero(&G.cache.arena, struct cache_node *, CACHE_BUCKETS_COUNT);
G.load_cmd_arena = arena_alloc(GIGABYTE(64));
G.load_cmd_mutex = sys_mutex_alloc();
G.load_cmd_or_shutdown_cv = sys_condition_variable_alloc();
/* Startup threads */
{
struct temp_arena scratch = scratch_begin_no_conflict();
G.loader_threads_count = clamp_i64(1, MAX_LOADER_THREADS, sys_num_logical_processors() - 1);
for (u64 i = 0; i < G.loader_threads_count; ++i) {
struct string thread_name = string_format(scratch.arena,
STR("[P0] Sheet loader thread %F"),
FMT_UINT(i));
G.loader_threads[i] = sys_thread_alloc(sheet_loader_thread_entry_point, NULL, thread_name);
}
scratch_end(scratch);
}
//G.evictor_thread = sys_thread_alloc(sheet_evictor_thread_entry_point, NULL, STR("[P0] Sheet evictor thread"));
app_register_exit_callback(&sheet_shutdown);
return (struct sheet_startup_receipt) { 0 };
}
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown)
{
__prof;
/* Signal shutdown */
sys_mutex_lock(&G.load_cmd_mutex);
{
G.shutdown = true;
sys_condition_variable_broadcast(&G.load_cmd_or_shutdown_cv);
}
sys_mutex_unlock(&G.load_cmd_mutex);
/* Wait on threads */
for (u64 i = 0; i < G.loader_threads_count; ++i) {
sys_thread_wait_release(&G.loader_threads[i]);
}
//sys_thread_wait_release(&G.evictor_thread);
}
/* ========================== *
* Tag
* ========================== */
@ -163,7 +214,7 @@ struct sheet_tag sheet_tag_from_path(struct string path)
{
struct sheet_tag res = { 0 };
res.hash = HASH_FNV64_SEED;
res.hash = hash_fnv64(&res.hash, BUFFER_FROM_STRING(path));
res.hash = hash_fnv64(res.hash, BUFFER_FROM_STRING(path));
res.path = path;
return res;
}
@ -175,44 +226,6 @@ INTERNAL struct sheet_tag sheet_tag_copy(struct arena *arena, struct sheet_tag s
return res;
}
/* ========================== *
* Load cmd
* ========================== */
#if 0
INTERNAL void push_load_cmd(struct cache_node *n, struct sheet_tag tag)
{
sys_mutex_lock(&G.load_cmd_store.mutex);
{
struct load_cmd *cmd = arena_push(&G.load_cmd_store.arena, struct load_cmd);
cmd->node = n;
cmd->tag = tag;
{
u64 copy_len = min_u64(tag.path.len, ARRAY_COUNT(cmd->tag_path_text));
cmd->tag.path.text = cmd->path_text_dest;
MEMCPY(cmd->tag.path.text, tag.path.text, copy_len);
}
}
sys_mutex_unlock(&G.load_cmd_store.mutex);
}
INTERNAL struct load_cmd_array pop_load_cmds(struct arena *arena)
{
struct load_cmd_array array = { 0 };
sys_mutex_lock(&G.load_cmd_store.mutex);
{
struct buffer cmds_buff = arena_to_buffer(&G.load_cmd_store.arena);
arena_align(arena, alignof(struct load_cmd));
array.cmds = (struct load_cmd *)arena_push_array(arena, u8, cmds_buff.size);
array.count = cmds_buff.size / sizeof(struct load_cmd);
MEMCPY(array.cmds, cmds_buff.data, cmds_buff.size);
arena_reset(&G.load_cmd_store.arena);
}
sys_mutex_unlock(&G.load_cmd_store.mutex);
return array;
}
#endif
/* ========================== *
* Load
* ========================== */
@ -369,8 +382,6 @@ void sheet_scope_end(struct sheet_scope *scope)
* Cache interface
* ========================== */
/* FIXME: locking */
INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct sheet_tag tag)
{
struct cache_node **n_next = NULL;
@ -418,7 +429,7 @@ INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct
INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct sheet_tag tag, b32 await)
{
struct sheet *res = &g_sheet_nil;
struct sheet *res = &g_sheet_loading;
struct cache_node *n = node_lookup_touch(scope, tag);
@ -427,13 +438,39 @@ INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct
res = &n->sheet;
} else if (state == CACHE_NODE_STATE_NONE) {
if (atomic_u32_eval_compare_exchange(&n->state, CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED) == CACHE_NODE_STATE_NONE) {
/* Node is new, load sheet */
if (await) {
sheet_load(n, tag);
res = &n->sheet;
} else {
/* TODO */
/* Node is new, push load command. */
//push_load_cmd(n, tag);
sys_mutex_lock(&G.load_cmd_mutex);
{
/* Allocate cmd */
struct load_cmd *cmd = NULL;
if (G.first_free_load_cmd) {
cmd = G.first_free_load_cmd;
G.first_free_load_cmd = cmd->next_free;
} else {
cmd = arena_push(&G.load_cmd_arena, struct load_cmd);
}
/* Initialize cmd */
cmd->cache_node = n;
cmd->tag = tag;
{
u64 copy_len = min_u64(tag.path.len, ARRAY_COUNT(cmd->tag_path_buff));
cmd->tag.path.text = cmd->tag_path_buff;
MEMCPY(cmd->tag.path.text, tag.path.text, copy_len);
}
/* Add cmd to queue */
*(G.last_load_cmd ? &G.last_load_cmd->next : &G.first_load_cmd) = cmd;
G.last_load_cmd = cmd;
/* Signal work ready */
sys_condition_variable_signal(&G.load_cmd_or_shutdown_cv);
}
sys_mutex_unlock(&G.load_cmd_mutex);
}
}
@ -470,6 +507,55 @@ struct sheet_span sheet_get_span(struct sheet *sheet, struct string name)
struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index)
{
if (sheet->frames_count > 0) {
index = min_u32(sheet->frames_count - 1, index);
return sheet->frames[index];
} else {
return (struct sheet_frame) {
.index = 0,
.duration = 0.1,
.clip = CLIP_ALL
};
}
}
/* ========================== *
* Loader thread
* ========================== */
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg)
{
(UNUSED)arg;
while (true) {
sys_mutex_lock(&G.load_cmd_mutex);
if (G.shutdown) {
/* Thread shutdown */
sys_mutex_unlock(&G.load_cmd_mutex);
break;
} else if (!G.first_load_cmd) {
/* Wait for work */
sys_condition_variable_wait(&G.load_cmd_or_shutdown_cv, &G.load_cmd_mutex);
}
if (G.first_load_cmd) {
/* Pull cmd from queue */
struct load_cmd *cmd = G.first_load_cmd;
G.first_load_cmd = cmd->next;
/* Do work (temporarily unlock) */
sys_mutex_unlock(&G.load_cmd_mutex);
{
sheet_load(cmd->cache_node, cmd->tag);
}
sys_mutex_lock(&G.load_cmd_mutex);
/* Free cmd */
cmd->next_free = G.first_free_load_cmd;
G.first_free_load_cmd = cmd;
}
sys_mutex_unlock(&G.load_cmd_mutex);
}
}

View File

@ -15,6 +15,7 @@ struct sheet_tag {
};
struct sheet {
b32 loading;
struct v2 image_size;
struct v2 frame_size;
u32 frames_count;

View File

@ -429,7 +429,7 @@ struct string string_formatv(struct arena *arena, struct string fmt, va_list arg
} break;
case FMT_TYPE_FLOAT: {
parsed_str = string_from_float(arena, arg.value.f, arg.precision);
parsed_str = string_from_float(arena, arg.value.f.val, arg.value.f.precision);
} break;
case FMT_TYPE_END: {

View File

@ -60,9 +60,11 @@ struct fmt_arg {
u64 uint;
i64 sint;
void *ptr;
f64 f;
} value;
struct {
f64 val;
u32 precision;
} f;
} value;
};
#define FMT_END (struct fmt_arg) {.type = FMT_TYPE_END}
@ -74,7 +76,7 @@ struct fmt_arg {
#define FMT_HEX(v) (struct fmt_arg) {.type = FMT_TYPE_HEX, .value.uint = (v)}
#define FMT_PTR(v) (struct fmt_arg) {.type = FMT_TYPE_PTR, .value.ptr = (v)}
#define FMT_FLOAT(v) FMT_FLOAT_P(v, DEFAULT_FMT_PRECISION)
#define FMT_FLOAT_P(v, p) (struct fmt_arg) {.type = FMT_TYPE_FLOAT, .value.f = (v), .precision = p}
#define FMT_FLOAT_P(v, p) (struct fmt_arg) {.type = FMT_TYPE_FLOAT, .value.f.val = (v), .value.f.precision = p}
#define string_format(arena, fmt, ...) _string_format(arena, fmt, __VA_ARGS__, FMT_END)
struct string _string_format(struct arena *arena, struct string fmt, ...);

View File

@ -771,7 +771,7 @@ INTERNAL void user_update(void)
{
struct sheet_tag sheet_tag = ent->sprite_sheet_tag;
struct sheet *sheet = sheet_from_tag_await(sheet_frame_scope, sheet_tag);
struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, sheet_tag);
struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
u64 frame_index = span.start + ent->animation_frame;

View File

@ -400,11 +400,13 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(worker_thread_entry_point, thread_data)
break;
}
/* Wait */
if (!G.scheduled_work_head) {
/* Wait for work */
sys_condition_variable_wait(&G.cv, &G.mutex);
}
/* Do work from top */
while (G.scheduled_work_head) {
if (G.scheduled_work_head) {
struct work *work = G.scheduled_work_head;
if (work) {
__profscope(work_pool_task);
@ -414,7 +416,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(worker_thread_entry_point, thread_data)
}
}
}
sys_mutex_unlock(&G.mutex);
}
}