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; f64 time_in_frame = ent->animation_time_in_frame + G.world.dt;
u64 span_frame_offset = ent->animation_frame; 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); struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
u64 frame_index = span.start + span_frame_offset; u64 frame_index = span.start + span_frame_offset;

View File

@ -10,6 +10,7 @@
#include "work.h" #include "work.h"
#include "atomic.h" #include "atomic.h"
#include "thread_local.h" #include "thread_local.h"
#include "app.h"
#define SHEET_ARENA_RESERVE MEGABYTE(64) #define SHEET_ARENA_RESERVE MEGABYTE(64)
#define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0 #define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0
@ -18,26 +19,21 @@
#define CACHE_BUCKETS_COUNT 256 #define CACHE_BUCKETS_COUNT 256
#define SCOPE_BUCKETS_COUNT 256 #define SCOPE_BUCKETS_COUNT 256
#define MAX_LOADER_THREADS 4
/* ========================== * /* ========================== *
* Load cmd structs * Load cmd structs
* ========================== */ * ========================== */
struct load_cmd { 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; struct sheet_tag tag;
u8 tag_path_buff[4096]; 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 * Cache structs
* ========================== */ * ========================== */
@ -83,11 +79,27 @@ struct sheet_scope_reference {
* ========================== */ * ========================== */
GLOBAL struct { GLOBAL struct {
struct load_cmd_store load_cmd_store; /* Cache */
struct cache 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); } 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 * Thread local state
@ -117,6 +129,9 @@ GLOBAL THREAD_LOCAL_VAR_DEF(tl_sheet_tctx, struct sheet_tctx, sheet_tctx_alloc,
* Startup * 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 sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
struct asset_cache_startup_receipt *asset_cache_sr, struct asset_cache_startup_receipt *asset_cache_sr,
struct resource_startup_receipt *resource_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)asset_cache_sr;
(UNUSED)resource_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.rw_mutex = sys_rw_mutex_alloc();
G.cache.arena = arena_alloc(GIGABYTE(64)); G.cache.arena = arena_alloc(GIGABYTE(64));
G.cache.buckets = arena_push_array_zero(&G.cache.arena, struct cache_node *, CACHE_BUCKETS_COUNT); 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 }; 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 * Tag
* ========================== */ * ========================== */
@ -163,7 +214,7 @@ struct sheet_tag sheet_tag_from_path(struct string path)
{ {
struct sheet_tag res = { 0 }; struct sheet_tag res = { 0 };
res.hash = HASH_FNV64_SEED; 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; res.path = path;
return res; return res;
} }
@ -175,44 +226,6 @@ INTERNAL struct sheet_tag sheet_tag_copy(struct arena *arena, struct sheet_tag s
return res; 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 * Load
* ========================== */ * ========================== */
@ -369,8 +382,6 @@ void sheet_scope_end(struct sheet_scope *scope)
* Cache interface * Cache interface
* ========================== */ * ========================== */
/* FIXME: locking */
INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct sheet_tag tag) INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct sheet_tag tag)
{ {
struct cache_node **n_next = NULL; 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) 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); 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; res = &n->sheet;
} else if (state == CACHE_NODE_STATE_NONE) { } 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) { 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) { if (await) {
sheet_load(n, tag); sheet_load(n, tag);
res = &n->sheet; res = &n->sheet;
} else { } else {
/* TODO */ sys_mutex_lock(&G.load_cmd_mutex);
/* Node is new, push load command. */ {
//push_load_cmd(n, tag); /* 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) struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index)
{ {
index = min_u32(sheet->frames_count - 1, index); if (sheet->frames_count > 0) {
return sheet->frames[index]; 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 { struct sheet {
b32 loading;
struct v2 image_size; struct v2 image_size;
struct v2 frame_size; struct v2 frame_size;
u32 frames_count; u32 frames_count;

View File

@ -429,7 +429,7 @@ struct string string_formatv(struct arena *arena, struct string fmt, va_list arg
} break; } break;
case FMT_TYPE_FLOAT: { 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; } break;
case FMT_TYPE_END: { case FMT_TYPE_END: {

View File

@ -60,9 +60,11 @@ struct fmt_arg {
u64 uint; u64 uint;
i64 sint; i64 sint;
void *ptr; void *ptr;
f64 f; struct {
f64 val;
u32 precision;
} f;
} value; } value;
u32 precision;
}; };
#define FMT_END (struct fmt_arg) {.type = FMT_TYPE_END} #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_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_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(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) #define string_format(arena, fmt, ...) _string_format(arena, fmt, __VA_ARGS__, FMT_END)
struct string _string_format(struct arena *arena, struct string fmt, ...); 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_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); struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
u64 frame_index = span.start + ent->animation_frame; 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; break;
} }
/* Wait */ if (!G.scheduled_work_head) {
sys_condition_variable_wait(&G.cv, &G.mutex); /* Wait for work */
sys_condition_variable_wait(&G.cv, &G.mutex);
}
/* Do work from top */ /* Do work from top */
while (G.scheduled_work_head) { if (G.scheduled_work_head) {
struct work *work = G.scheduled_work_head; struct work *work = G.scheduled_work_head;
if (work) { if (work) {
__profscope(work_pool_task); __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); sys_mutex_unlock(&G.mutex);
} }
} }