From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- src/asset/asset_cache.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 src/asset/asset_cache.c (limited to 'src/asset/asset_cache.c') diff --git a/src/asset/asset_cache.c b/src/asset/asset_cache.c new file mode 100644 index 0000000..16c4d5c --- /dev/null +++ b/src/asset/asset_cache.c @@ -0,0 +1,252 @@ +#include "asset_cache.h" + +#include "model.h" +#include "scene/animation_impl.h" +#include "scene/model_impl.h" +#include "scene/node_impl.h" +#include "scene/scene_memory.h" +#include "texture.h" + +#include +#include +#include +#include + +#include +#include +#include + +static void log_model_load_failure(const LoadModelCmd* cmd) { + assert(cmd); + + switch (cmd->origin) { + case AssetFromFile: + log_error("Failed to load model: %s", mstring_cstr(&cmd->filepath)); + break; + case AssetFromMemory: + log_error("Failed to load model: %p", cmd->data); + break; + } +} + +static void log_texture_load_failure(const LoadTextureCmd* cmd) { + assert(cmd); + + switch (cmd->origin) { + case AssetFromFile: + switch (cmd->type) { + case LoadTexture: + log_error( + "Failed to load texture: %s", + mstring_cstr(&cmd->data.texture.filepath)); + break; + case LoadCubemap: + log_error( + "Failed to load cubemap texture: %s", + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x)); + break; + } + break; + case AssetFromMemory: + switch (cmd->type) { + case LoadTexture: + log_error("Failed to load texture: %p", cmd->data.texture.data); + break; + case LoadCubemap: + log_error( + "Failed to load texture: %p", cmd->data.cubemap.buffers.data_pos_x); + break; + } + break; + } +} + +static Hash calc_model_hash(const LoadModelCmd* cmd) { + assert(cmd); + switch (cmd->origin) { + case AssetFromFile: + return cstring_hash(mstring_cstr(&cmd->filepath)); + case AssetFromMemory: + return (Hash)cmd->data; + } + FAIL("Unhandled model asset origin"); + return 0; +} + +static Hash calc_texture_hash(const LoadTextureCmd* cmd) { + assert(cmd); + switch (cmd->origin) { + case AssetFromFile: + switch (cmd->type) { + case LoadTexture: + return cstring_hash(mstring_cstr(&cmd->data.texture.filepath)); + case LoadCubemap: + return cstring_hash( + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x)) ^ + cstring_hash( + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_x)) ^ + cstring_hash( + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_y)) ^ + cstring_hash( + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_y)) ^ + cstring_hash( + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_z)) ^ + cstring_hash( + mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_z)); + } + break; + case AssetFromMemory: + switch (cmd->type) { + case LoadTexture: + return (Hash)cmd->data.texture.data; + case LoadCubemap: + return (Hash)cmd->data.cubemap.buffers.data_pos_x ^ + (Hash)cmd->data.cubemap.buffers.data_neg_x ^ + (Hash)cmd->data.cubemap.buffers.data_pos_y ^ + (Hash)cmd->data.cubemap.buffers.data_neg_y ^ + (Hash)cmd->data.cubemap.buffers.data_pos_z ^ + (Hash)cmd->data.cubemap.buffers.data_neg_z; + } + break; + } + FAIL("Unhandled texture asset origin"); + return 0; +} + +static Asset* lookup_cache(AssetCache* cache, Hash hash) { + assert(cache); + mempool_foreach(&cache->assets, asset, { + if (asset->hash == hash) { + return asset; + } + }); + return 0; +} + +static void log_model_cache_hit(const LoadModelCmd* cmd, Hash hash) { + assert(cmd); + switch (cmd->origin) { + case AssetFromFile: + LOGD( + "Found asset [%s] in cache with hash [%lu]", + mstring_cstr(&cmd->filepath), hash); + break; + case AssetFromMemory: + LOGD("Found asset [%p] in cache with hash [%lu]", cmd->data, hash); + break; + } +} + +static void log_model_loaded(const LoadModelCmd* cmd) { + assert(cmd); + switch (cmd->origin) { + case AssetFromFile: + LOGD("Loaded asset from file: [%s]", mstring_cstr(&cmd->filepath)); + break; + case AssetFromMemory: + LOGD("Loaded asset from memory: [%p]", cmd->data); + break; + } +} + +static Model* clone_model(const Model* model) { + assert(model); + + // Only the Anima needs to be cloned since everything else in the model is + // static. + // + // The Anima can be partially shallow-cloned. Skeletons and animations are + // static and can be shared with the original Anima. Other members are + // deep-cloned. Skeletons in particular point back to their Anima, so need to + // be deep-cloned. + const SceneNode* root = mem_get_node(model->root); + if (gfx_get_node_type(root) == AnimaNode) { + const Anima* anima = gfx_get_node_anima(root); + Anima* anima_copy = mem_alloc_anima(); + *anima_copy = *anima; // Shallow copy. + + SceneNode* root_copy = gfx_clone_scene_shallow(root); + root_copy->anima = mem_get_anima_index(anima_copy); + anima_copy->parent = mem_get_node_index(root_copy); + + Model* copy = mem_alloc_model(); + copy->root = mem_get_node_index(root_copy); + return copy; + } else { + return (Model*)model; // Static model, can't be mutated. + } +} + +void gfx_init_asset_cache(AssetCache* cache) { + assert(cache); + mempool_make(&cache->assets); + + // Allocate a dummy asset at index 0 to guarantee that no assets allocated by + // the caller map to index 0. + const Asset* dummy = mempool_alloc(&cache->assets); + assert(mempool_get_block_index(&cache->assets, dummy) == 0); +} + +void gfx_destroy_asset_cache(AssetCache* cache) { + assert(cache); + mempool_del(&cache->assets); +} + +Model* gfx_load_model(Gfx* gfx, const LoadModelCmd* cmd) { + assert(gfx); + + AssetCache* cache = gfx_get_asset_cache(gfx); + + // First search for the asset in the cache. + const uint64_t hash = calc_model_hash(cmd); + Asset* asset = lookup_cache(cache, hash); + if (asset) { + log_model_cache_hit(cmd, hash); + return clone_model(asset->model); + } + + // Asset not found in the cache. + // Load it, insert it into the cache, and return it. + Model* model = gfx_model_load(gfx, cmd); + if (model) { + *(Asset*)mempool_alloc(&cache->assets) = (Asset){ + .type = ModelAsset, + .hash = hash, + .model = model, + }; + log_model_loaded(cmd); + return clone_model(model); + } else { + log_model_load_failure(cmd); + return 0; + } +} + +const Texture* gfx_load_texture(Gfx* gfx, const LoadTextureCmd* cmd) { + assert(gfx); + assert(cmd); + + AssetCache* cache = gfx_get_asset_cache(gfx); + + // First search for the asset in the cache. + const uint64_t hash = calc_texture_hash(cmd); + Asset* asset = lookup_cache(cache, hash); + if (asset) { + return asset->texture; + } + + // Asset not found in the cache. + // Load it, insert it into the cache, and return it. + GfxCore* gfxcore = gfx_get_core(gfx); + const Texture* texture = gfx_texture_load(gfxcore, cmd); + if (texture) { + *(Asset*)mempool_alloc(&cache->assets) = (Asset){ + .type = TextureAsset, + .hash = hash, + .texture = texture, + }; + } else { + log_texture_load_failure(cmd); + } + return texture; +} -- cgit v1.2.3