diff options
Diffstat (limited to 'src/asset/asset_cache.c')
-rw-r--r-- | src/asset/asset_cache.c | 252 |
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 | |||
19 | static 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 | |||
32 | static 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 | |||
64 | static 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 | |||
76 | static 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 | |||
116 | static 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 | |||
126 | static 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 | |||
140 | static 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 | |||
152 | static 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 | |||
180 | void 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 | |||
190 | void gfx_destroy_asset_cache(AssetCache* cache) { | ||
191 | assert(cache); | ||
192 | mempool_del(&cache->assets); | ||
193 | } | ||
194 | |||
195 | Model* 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 | |||
225 | const 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 | } | ||