power_play/src/sheet.c

288 lines
8.0 KiB
C

#include "sheet.h"
#include "arena.h"
#include "log.h"
#include "sys.h"
#include "scratch.h"
#include "resource.h"
#include "asset_cache.h"
#include "ase.h"
#include "util.h"
struct sheet_task_params {
struct sheet_task_params *next_free;
struct asset *asset;
u64 path_len;
char path_cstr[1024];
};
struct sheet_task_params_store {
struct sheet_task_params *head_free;
struct arena arena;
struct sys_mutex mutex;
};
/* ========================== *
* Global state
* ========================== */
GLOBAL struct {
struct sheet_task_params_store params;
} L = { 0 }, DEBUG_LVAR(L_sheet);
/* ========================== *
* Startup
* ========================== */
void sheet_startup(void)
{
L.params.arena = arena_alloc(GIGABYTE(64));
L.params.mutex = sys_mutex_alloc();
}
/* ========================== *
* Load task param store
* ========================== */
INTERNAL struct sheet_task_params *sheet_task_params_alloc(void)
{
struct sheet_task_params *p = NULL;
sys_mutex_lock(&L.params.mutex);
{
if (L.params.head_free) {
p = L.params.head_free;
L.params.head_free = p->next_free;
} else {
p = arena_push_zero(&L.params.arena, struct sheet_task_params);
}
}
sys_mutex_unlock(&L.params.mutex);
return p;
}
INTERNAL void sheet_task_params_release(struct sheet_task_params *p)
{
sys_mutex_lock(&L.params.mutex);
{
p->next_free = L.params.head_free;
L.params.head_free = p;
}
sys_mutex_unlock(&L.params.mutex);
}
/* ========================== *
* Init
* ========================== */
#define SHEET_LOOKUP_TABLE_CAPACITY_FACTOR 2.0
INTERNAL struct sheet sheet_from_ase(struct arena *arena, struct ase_decode_sheet_result ase)
{
struct sheet sheet = { 0 };
ASSERT(ase.num_frames >= 1);
/* Init frames */
sheet.image_size = ase.image_size;
sheet.frame_size = ase.frame_size;
sheet.frames = arena_push_array_zero(arena, struct sheet_frame, ase.num_frames);
sheet.frames_count = ase.num_frames;
for (struct ase_frame *ase_frame = ase.frame_head; ase_frame; ase_frame = ase_frame->next) {
u32 index = ase_frame->index;
sheet.frames[index] = (struct sheet_frame) {
.index = index,
.duration = ase_frame->duration,
.clip = ase_frame->clip
};
}
/* Init tags */
sheet.tags_count = ase.num_tags;
if (ase.num_tags > 0) {
sheet.tags_dict = fixed_dict_init(arena, (u64)(ase.num_tags * SHEET_LOOKUP_TABLE_CAPACITY_FACTOR));
for (struct ase_tag *ase_tag = ase.tag_head; ase_tag; ase_tag = ase_tag->next) {
struct string name = string_copy(arena, ase_tag->name);
struct sheet_tag *tag = arena_push(arena, struct sheet_tag);
*tag = (struct sheet_tag) {
.name = name,
.start = ase_tag->start,
.end = ase_tag->end
};
fixed_dict_set(arena, &sheet.tags_dict, name, tag);
}
}
return sheet;
}
INTERNAL struct sheet sheet_default(struct arena *arena)
{
struct sheet sheet = { 0 };
sheet.frames_count = 1;
sheet.frames = arena_push(arena, struct sheet_frame);
sheet.frames[0] = (struct sheet_frame) {
.index = 0,
.duration = 0,
.clip = CLIP_ALL
};
return sheet;
}
/* ========================== *
* Load
* ========================== */
INTERNAL WORK_TASK_FUNC_DEF(sheet_load_asset_task, vparams)
{
__prof;
struct sheet_task_params *params = (struct sheet_task_params *)vparams;
struct temp_arena scratch = scratch_begin_no_conflict();
struct string path = string_from_cstr_len(params->path_cstr, params->path_len);
struct asset *asset = params->asset;
logf_info("Loading sheet \"%F\"", FMT_STR(path));
sys_timestamp_t start_ts = sys_timestamp();
b32 success = false;
struct string error_msg = STR("Unknown error");
ASSERT(string_ends_with(path, STR(".ase")));
if (resource_exists(path)) {
/* Decode */
struct resource sheet_rs = resource_open(path);
struct ase_decode_sheet_result decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes);
resource_close(sheet_rs);
/* Failure paths */
if (!decoded.valid) {
if (decoded.error_msg.len > 0) {
error_msg = decoded.error_msg;
}
goto abort;
} else {
success = true;
}
/* Initialize sheet & its data into store */
struct sheet *sheet = NULL;
{
struct asset_cache_store store = asset_cache_store_open();
sheet = arena_push(store.arena, struct sheet);
*sheet = sheet_from_ase(store.arena, decoded);
asset_cache_store_close(&store);
}
logf_info("Finished loading sheet \"%F\" in %F seconds",
FMT_STR(path),
FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)));
asset_cache_mark_ready(asset, sheet);
} else {
success = false;
error_msg = STR("Resource not found");
goto abort;
}
abort:
if (!success) {
logf_error("Error loading sheet \"%F\": %F", FMT_STR(path), FMT_STR(error_msg));
/* Store */
struct sheet *sheet = NULL;
{
struct asset_cache_store store = asset_cache_store_open();
sheet = arena_push(store.arena, struct sheet);
*sheet = sheet_default(store.arena);
asset_cache_store_close(&store);
}
asset_cache_mark_ready(asset, sheet);
}
sheet_task_params_release(params);
/* Decommit decoded sheet data */
scratch_end_and_decommit(scratch);
}
struct asset *sheet_load_asset(struct string path, b32 help)
{
__prof;
struct temp_arena scratch = scratch_begin_no_conflict();
struct string key = string_cat(scratch.arena, path, STR("_sheet"));
u64 hash = asset_cache_hash(key);
b32 is_first_touch;
struct asset *asset = asset_cache_touch(key, hash, &is_first_touch);
if (is_first_touch) {
/* Assemble task params */
struct sheet_task_params *params = sheet_task_params_alloc();
if (path.len > (sizeof(params->path_cstr) - 1)) {
sys_panic(string_format(scratch.arena,
STR("Sheet path \"%F\" too long!"),
FMT_STR(path)));
}
string_to_cstr_buff(path, BUFFER_FROM_ARRAY(params->path_cstr));
params->path_len = path.len;
params->asset = asset;
/* Push task */
asset_cache_mark_loading(asset);
struct work_handle wh = { 0 };
if (help) {
wh = work_push_task_and_help(&sheet_load_asset_task, params, WORK_PRIORITY_NORMAL);
} else {
wh = work_push_task(&sheet_load_asset_task, params, WORK_PRIORITY_NORMAL);
}
asset_cache_set_work(asset, &wh);
}
scratch_end(scratch);
return asset;
}
struct sheet *sheet_load_async(struct string path)
{
__prof;
struct asset *asset = sheet_load_asset(path, false);
struct sheet *sheet = (struct sheet *)asset_cache_get_store_data(asset);
return sheet;
}
struct sheet *sheet_load(struct string path)
{
__prof;
struct asset *asset = sheet_load_asset(path, true);
asset_cache_wait(asset);
struct sheet *sheet = (struct sheet *)asset_cache_get_store_data(asset);
return sheet;
}
/* ========================== *
* Sheet data
* ========================== */
GLOBAL READONLY struct sheet_tag g_default_tag = { 0 };
struct sheet_tag sheet_get_tag(struct sheet *sheet, struct string name)
{
struct sheet_tag res = g_default_tag;
if (sheet->tags_count > 0) {
struct sheet_tag *entry = fixed_dict_get(&sheet->tags_dict, name);
if (entry) {
res = *entry;
}
}
return res;
}
struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index)
{
index = min_u32(sheet->frames_count - 1, index);
return sheet->frames[index];
}