aboutsummaryrefslogtreecommitdiff
path: root/src/asset/asset_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/asset/asset_cache.c')
-rw-r--r--src/asset/asset_cache.c252
1 files changed, 252 insertions, 0 deletions
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 @@
1#include "asset_cache.h"
2
3#include "model.h"
4#include "scene/animation_impl.h"
5#include "scene/model_impl.h"
6#include "scene/node_impl.h"
7#include "scene/scene_memory.h"
8#include "texture.h"
9
10#include <gfx/asset.h>
11#include <gfx/gfx.h>
12#include <gfx/scene/node.h>
13#include <gfx_assert.h>
14
15#include <cstring.h>
16#include <error.h>
17#include <log/log.h>
18
19static void log_model_load_failure(const LoadModelCmd* cmd) {
20 assert(cmd);
21
22 switch (cmd->origin) {
23 case AssetFromFile:
24 log_error("Failed to load model: %s", mstring_cstr(&cmd->filepath));
25 break;
26 case AssetFromMemory:
27 log_error("Failed to load model: %p", cmd->data);
28 break;
29 }
30}
31
32static void log_texture_load_failure(const LoadTextureCmd* cmd) {
33 assert(cmd);
34
35 switch (cmd->origin) {
36 case AssetFromFile:
37 switch (cmd->type) {
38 case LoadTexture:
39 log_error(
40 "Failed to load texture: %s",
41 mstring_cstr(&cmd->data.texture.filepath));
42 break;
43 case LoadCubemap:
44 log_error(
45 "Failed to load cubemap texture: %s",
46 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x));
47 break;
48 }
49 break;
50 case AssetFromMemory:
51 switch (cmd->type) {
52 case LoadTexture:
53 log_error("Failed to load texture: %p", cmd->data.texture.data);
54 break;
55 case LoadCubemap:
56 log_error(
57 "Failed to load texture: %p", cmd->data.cubemap.buffers.data_pos_x);
58 break;
59 }
60 break;
61 }
62}
63
64static Hash calc_model_hash(const LoadModelCmd* cmd) {
65 assert(cmd);
66 switch (cmd->origin) {
67 case AssetFromFile:
68 return cstring_hash(mstring_cstr(&cmd->filepath));
69 case AssetFromMemory:
70 return (Hash)cmd->data;
71 }
72 FAIL("Unhandled model asset origin");
73 return 0;
74}
75
76static Hash calc_texture_hash(const LoadTextureCmd* cmd) {
77 assert(cmd);
78 switch (cmd->origin) {
79 case AssetFromFile:
80 switch (cmd->type) {
81 case LoadTexture:
82 return cstring_hash(mstring_cstr(&cmd->data.texture.filepath));
83 case LoadCubemap:
84 return cstring_hash(
85 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x)) ^
86 cstring_hash(
87 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_x)) ^
88 cstring_hash(
89 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_y)) ^
90 cstring_hash(
91 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_y)) ^
92 cstring_hash(
93 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_z)) ^
94 cstring_hash(
95 mstring_cstr(&cmd->data.cubemap.filepaths.filepath_neg_z));
96 }
97 break;
98 case AssetFromMemory:
99 switch (cmd->type) {
100 case LoadTexture:
101 return (Hash)cmd->data.texture.data;
102 case LoadCubemap:
103 return (Hash)cmd->data.cubemap.buffers.data_pos_x ^
104 (Hash)cmd->data.cubemap.buffers.data_neg_x ^
105 (Hash)cmd->data.cubemap.buffers.data_pos_y ^
106 (Hash)cmd->data.cubemap.buffers.data_neg_y ^
107 (Hash)cmd->data.cubemap.buffers.data_pos_z ^
108 (Hash)cmd->data.cubemap.buffers.data_neg_z;
109 }
110 break;
111 }
112 FAIL("Unhandled texture asset origin");
113 return 0;
114}
115
116static Asset* lookup_cache(AssetCache* cache, Hash hash) {
117 assert(cache);
118 mempool_foreach(&cache->assets, asset, {
119 if (asset->hash == hash) {
120 return asset;
121 }
122 });
123 return 0;
124}
125
126static void log_model_cache_hit(const LoadModelCmd* cmd, Hash hash) {
127 assert(cmd);
128 switch (cmd->origin) {
129 case AssetFromFile:
130 LOGD(
131 "Found asset [%s] in cache with hash [%lu]",
132 mstring_cstr(&cmd->filepath), hash);
133 break;
134 case AssetFromMemory:
135 LOGD("Found asset [%p] in cache with hash [%lu]", cmd->data, hash);
136 break;
137 }
138}
139
140static void log_model_loaded(const LoadModelCmd* cmd) {
141 assert(cmd);
142 switch (cmd->origin) {
143 case AssetFromFile:
144 LOGD("Loaded asset from file: [%s]", mstring_cstr(&cmd->filepath));
145 break;
146 case AssetFromMemory:
147 LOGD("Loaded asset from memory: [%p]", cmd->data);
148 break;
149 }
150}
151
152static Model* clone_model(const Model* model) {
153 assert(model);
154
155 // Only the Anima needs to be cloned since everything else in the model is
156 // static.
157 //
158 // The Anima can be partially shallow-cloned. Skeletons and animations are
159 // static and can be shared with the original Anima. Other members are
160 // deep-cloned. Skeletons in particular point back to their Anima, so need to
161 // be deep-cloned.
162 const SceneNode* root = mem_get_node(model->root);
163 if (gfx_get_node_type(root) == AnimaNode) {
164 const Anima* anima = gfx_get_node_anima(root);
165 Anima* anima_copy = mem_alloc_anima();
166 *anima_copy = *anima; // Shallow copy.
167
168 SceneNode* root_copy = gfx_clone_scene_shallow(root);
169 root_copy->anima = mem_get_anima_index(anima_copy);
170 anima_copy->parent = mem_get_node_index(root_copy);
171
172 Model* copy = mem_alloc_model();
173 copy->root = mem_get_node_index(root_copy);
174 return copy;
175 } else {
176 return (Model*)model; // Static model, can't be mutated.
177 }
178}
179
180void gfx_init_asset_cache(AssetCache* cache) {
181 assert(cache);
182 mempool_make(&cache->assets);
183
184 // Allocate a dummy asset at index 0 to guarantee that no assets allocated by
185 // the caller map to index 0.
186 const Asset* dummy = mempool_alloc(&cache->assets);
187 assert(mempool_get_block_index(&cache->assets, dummy) == 0);
188}
189
190void gfx_destroy_asset_cache(AssetCache* cache) {
191 assert(cache);
192 mempool_del(&cache->assets);
193}
194
195Model* gfx_load_model(Gfx* gfx, const LoadModelCmd* cmd) {
196 assert(gfx);
197
198 AssetCache* cache = gfx_get_asset_cache(gfx);
199
200 // First search for the asset in the cache.
201 const uint64_t hash = calc_model_hash(cmd);
202 Asset* asset = lookup_cache(cache, hash);
203 if (asset) {
204 log_model_cache_hit(cmd, hash);
205 return clone_model(asset->model);
206 }
207
208 // Asset not found in the cache.
209 // Load it, insert it into the cache, and return it.
210 Model* model = gfx_model_load(gfx, cmd);
211 if (model) {
212 *(Asset*)mempool_alloc(&cache->assets) = (Asset){
213 .type = ModelAsset,
214 .hash = hash,
215 .model = model,
216 };
217 log_model_loaded(cmd);
218 return clone_model(model);
219 } else {
220 log_model_load_failure(cmd);
221 return 0;
222 }
223}
224
225const Texture* gfx_load_texture(Gfx* gfx, const LoadTextureCmd* cmd) {
226 assert(gfx);
227 assert(cmd);
228
229 AssetCache* cache = gfx_get_asset_cache(gfx);
230
231 // First search for the asset in the cache.
232 const uint64_t hash = calc_texture_hash(cmd);
233 Asset* asset = lookup_cache(cache, hash);
234 if (asset) {
235 return asset->texture;
236 }
237
238 // Asset not found in the cache.
239 // Load it, insert it into the cache, and return it.
240 GfxCore* gfxcore = gfx_get_core(gfx);
241 const Texture* texture = gfx_texture_load(gfxcore, cmd);
242 if (texture) {
243 *(Asset*)mempool_alloc(&cache->assets) = (Asset){
244 .type = TextureAsset,
245 .hash = hash,
246 .texture = texture,
247 };
248 } else {
249 log_texture_load_failure(cmd);
250 }
251 return texture;
252}