Unravel Engine C++ Reference
Loading...
Searching...
No Matches
asset_storage.h
Go to the documentation of this file.
1#pragma once
2
3#include "asset_handle.h"
4#include "asset_flags.h"
5
6#include <context/context.hpp>
7#include <hpp/event.hpp>
8
9#include <cassert>
10#include <functional>
11#include <map>
12#include <mutex>
13#include <unordered_map>
14
16
17namespace unravel
18{
19
20struct asset_importer_meta : crtp_meta_type<asset_importer_meta>
21{
22 virtual ~asset_importer_meta() = default;
23};
24
73
74struct mesh_importer_meta : crtp_meta_type<mesh_importer_meta, asset_importer_meta>
75{
77 {
78 bool import_meshes{true};
79 bool weld_vertices{true};
80 bool optimize_meshes{true};
82 bool find_degenerates{true};
84
86 bool generate_lods{true};
88 float lod_target_error{0.01f};
90
91 struct rig_meta
92 {
93
94 } rig;
95
101
108};
109
110struct animation_importer_meta : crtp_meta_type<animation_importer_meta, asset_importer_meta>
111{
112
122};
123
124struct audio_importer_meta : crtp_meta_type<audio_importer_meta, asset_importer_meta>
125{
126 bool force_to_mono{false};
127};
128
129//-----------------------------------------------------------------------------
130// Asset header info -- lightweight per-type metadata readable without full load.
131// Populated during compilation, stored in .meta files (editor only).
132//-----------------------------------------------------------------------------
133
135struct asset_header_info : crtp_meta_type<asset_header_info>
136{
137 virtual ~asset_header_info() = default;
138 size_t file_size{};
139};
140
141struct texture_header_info : crtp_meta_type<texture_header_info, asset_header_info>
142{
143 uint32_t width{};
144 uint32_t height{};
145 uint16_t depth{1};
146 uint16_t num_layers{1};
147 uint16_t num_mips{1};
148 std::string format{};
149};
150
151struct mesh_header_info : crtp_meta_type<mesh_header_info, asset_header_info>
152{
153 uint32_t vertex_count{};
154 uint32_t index_count{};
155 uint32_t submesh_count{};
156 uint32_t lod_count{};
157};
158
159struct animation_header_info : crtp_meta_type<animation_header_info, asset_header_info>
160{
161 float duration{};
162 uint32_t channel_count{};
163 float sample_rate{};
164};
165
166struct audio_header_info : crtp_meta_type<audio_header_info, asset_header_info>
167{
168 float duration{};
169 uint32_t sample_rate{};
170 uint16_t channels{};
171};
172
178{
180 hpp::uuid uid{};
182 std::string type{};
184 std::shared_ptr<asset_importer_meta> importer;
186 std::shared_ptr<asset_header_info> header;
187};
188
194{
195public:
200 struct meta
201 {
203 std::string location{};
206 };
207
209 using database_t = std::map<hpp::uuid, meta>;
210
215 auto get_database() const -> const database_t&
216 {
217 return asset_meta_;
218 }
219
224 void set_database(const database_t& rhs)
225 {
226 std::lock_guard<std::mutex> lock(asset_mutex_);
227 asset_meta_ = rhs;
228 }
229
234 {
235 std::lock_guard<std::mutex> lock(asset_mutex_);
236 asset_meta_.clear();
237 }
238
263 auto add_asset(const std::string& location, const asset_meta& meta, bool override) -> hpp::uuid
264 {
265 std::lock_guard<std::mutex> lock(asset_mutex_);
266
267 auto existing = find_entry_by_location_unlocked(location);
268 if(existing != asset_meta_.end())
269 {
270 if(!override)
271 {
272 return existing->first;
273 }
274
275 // Update in place, preserving the existing UUID so that other
276 // assets that reference it (e.g. materials → textures) don't get
277 // orphaned by an override pass.
278 existing->second.location = location;
279 existing->second.meta = meta;
280 existing->second.meta.uid = existing->first;
281 return existing->first;
282 }
283
284 auto& metainfo = asset_meta_[meta.uid];
285 metainfo.location = location;
286 metainfo.meta = meta;
287 // APPLOG_TRACE("{} - {} -> {}", __func__, hpp::to_string(metainfo.meta.uid), location);
288 return metainfo.meta.uid;
289 }
290
296 auto get_uuid(const std::string& location) const -> const hpp::uuid&
297 {
298 std::lock_guard<std::mutex> lock(asset_mutex_);
299
300 auto it = find_entry_by_location_unlocked(location);
301 if(it != asset_meta_.end())
302 {
303 return it->first;
304 }
305
306 static const hpp::uuid uid;
307 return uid;
308 }
309
310 auto get_metadata(const std::string& location) const -> const meta&
311 {
312 std::lock_guard<std::mutex> lock(asset_mutex_);
313
314 auto it = find_entry_by_location_unlocked(location);
315 if(it != asset_meta_.end())
316 {
317 return it->second;
318 }
319
320 static const meta empty;
321 return empty;
322 }
323
329 auto get_metadata(const hpp::uuid& id) const -> const meta&
330 {
331 std::lock_guard<std::mutex> lock(asset_mutex_);
332
333 auto it = asset_meta_.find(id);
334 if(it == asset_meta_.end())
335 {
336 static const meta empty;
337 return empty;
338 }
339
340 return it->second;
341 }
342
348 void rename_asset(const std::string& key, const std::string& new_key)
349 {
350 std::lock_guard<std::mutex> lock(asset_mutex_);
351 for(auto& kvp : asset_meta_)
352 {
353 auto& uid = kvp.first;
354 auto& metainfo = kvp.second;
355 if(metainfo.location == key)
356 {
357 APPLOG_TRACE("{}::{} - {} -> {}", __func__, hpp::to_string(uid), key, new_key);
358
359 metainfo.location = new_key;
360 }
361 }
362 }
363
368 void remove_asset(const std::string& key)
369 {
370 std::lock_guard<std::mutex> lock(asset_mutex_);
371 for(auto& kvp : asset_meta_)
372 {
373 auto& uid = kvp.first;
374 auto& metainfo = kvp.second;
375 if(metainfo.location == key)
376 {
377 APPLOG_TRACE("{}::{} - {}", __func__, hpp::to_string(uid), key);
378
379 asset_meta_.erase(uid);
380 return;
381 }
382 }
383 }
384
385private:
393 auto find_entry_by_location_unlocked(const std::string& location) -> database_t::iterator
394 {
395 for(auto it = asset_meta_.begin(); it != asset_meta_.end(); ++it)
396 {
397 if(it->second.location == location)
398 {
399 return it;
400 }
401 }
402 return asset_meta_.end();
403 }
404
405 auto find_entry_by_location_unlocked(const std::string& location) const -> database_t::const_iterator
406 {
407 for(auto it = asset_meta_.cbegin(); it != asset_meta_.cend(); ++it)
408 {
409 if(it->second.location == location)
410 {
411 return it;
412 }
413 }
414 return asset_meta_.cend();
415 }
416
418 mutable std::mutex asset_mutex_{};
420 database_t asset_meta_{};
421};
422
428{
429 virtual ~basic_storage() = default;
430
435 virtual void unload_all(tpp::thread_pool& pool) = 0;
436
442 virtual void unload_single(tpp::thread_pool& pool, const std::string& key) = 0;
443
449 virtual void unload_group(tpp::thread_pool& pool, const std::string& group) = 0;
450
457 virtual void evict_unused(tpp::thread_pool& pool, const std::string& group, std::chrono::steady_clock::duration max_idle) = 0;
458
463 virtual void preload_all() = 0;
464};
465
471template<typename T>
473{
475 using request_container_t = std::unordered_map<std::string, asset_handle<T>>;
477 template<typename F>
478 using callable = std::function<F>;
479
481 using load_from_file_t = callable<bool(tpp::thread_pool& pool, asset_handle<T>&, const std::string&, load_mode)>;
482
484 using predicate_t = callable<bool(const asset_handle<T>&)>;
485 using load_from_instance_t = callable<bool(tpp::thread_pool& pool, asset_handle<T>&, std::shared_ptr<T>)>;
486
487 ~asset_storage() override = default;
488
494 void unload_handle(tpp::thread_pool& pool, asset_handle<T>& handle)
495 {
496 pool.stop(handle.task_id());
497 handle.invalidate();
498 }
499
505 void unload_with_condition(tpp::thread_pool& pool, const predicate_t& predicate)
506 {
507 std::lock_guard<std::recursive_mutex> lock(container_mutex);
508 for(auto it = container.begin(); it != container.end();)
509 {
510 if(predicate(it->second))
511 {
512 auto& handle = it->second;
513 unload_handle(pool, handle);
514 it = container.erase(it);
515 }
516 else
517 {
518 ++it;
519 }
520 }
521 }
522
527 void unload_all(tpp::thread_pool& pool) final
528 {
530 [](const auto& it)
531 {
532 return true;
533 });
534 }
535
541 void unload_group(tpp::thread_pool& pool, const std::string& group) final
542 {
544 [&](const auto& it)
545 {
546 const auto& id = it.id();
547 hpp::string_view id_view(id);
548 return id_view.starts_with(group);
549 });
550 }
551
557 void unload_single(tpp::thread_pool& pool, const std::string& key) final
558 {
560 [&](const auto& it)
561 {
562 const auto& id = it.id();
563 return id == key;
564 });
565 }
566
572 auto get_with_condition(const predicate_t& predicate) const -> std::vector<asset_handle<T>>
573 {
574 std::lock_guard<std::recursive_mutex> lock(container_mutex);
575 std::vector<asset_handle<T>> result;
576 result.reserve(container.size() + 1);
577 result.emplace_back(asset_handle<T>::get_empty());
578
579 for(const auto& kvp : container)
580 {
581 if(predicate(kvp.second))
582 {
583 result.emplace_back(kvp.second);
584 }
585 }
586
587 return result;
588 }
589
595 auto get_group(const std::string& group) const -> std::vector<asset_handle<T>>
596 {
597 return get_with_condition(
598 [&](const auto& it)
599 {
600 const auto& id = it.id();
601 hpp::string_view id_view(id);
602 return id_view.starts_with(group);
603 });
604 }
605
613 void evict_unused(tpp::thread_pool& pool, const std::string& group, std::chrono::steady_clock::duration max_idle) final
614 {
615 auto now = std::chrono::steady_clock::now();
616 std::lock_guard<std::recursive_mutex> lock(container_mutex);
617 for(auto& [key, handle] : container)
618 {
619 const auto& id = handle.id();
620 hpp::string_view id_view(id);
621 if(!id_view.starts_with(group))
622 {
623 continue;
624 }
625 if(!handle.is_ready())
626 {
627 continue;
628 }
629 bool is_idle = (now - handle.last_access()) > max_idle;
630 if(!is_idle)
631 {
632 continue;
633 }
634 pool.stop(handle.task_id());
635 handle.demote_to_deferred();
637 }
638 }
639
640 void preload_all() final
641 {
642 std::lock_guard<std::recursive_mutex> lock(container_mutex);
643 for(auto& [key, handle] : container)
644 {
645 if(handle.is_deferred())
646 {
647 handle.submit();
648 }
649 }
650 }
651
659 mutable std::recursive_mutex container_mutex;
660};
661
662} // namespace unravel
Manages asset metadata and provides functionality for adding, removing, and querying assets.
void remove_asset(const std::string &key)
Removes an asset from the database.
void rename_asset(const std::string &key, const std::string &new_key)
Renames an asset.
void set_database(const database_t &rhs)
Sets the asset database.
auto get_metadata(const hpp::uuid &id) const -> const meta &
Gets the metadata of an asset based on its UUID.
auto get_metadata(const std::string &location) const -> const meta &
void remove_all()
Removes all assets from the database.
auto get_database() const -> const database_t &
Gets the entire asset database.
std::map< hpp::uuid, meta > database_t
Type definition for the asset database.
auto get_uuid(const std::string &location) const -> const hpp::uuid &
Gets the UUID of an asset based on its location.
auto add_asset(const std::string &location, const asset_meta &meta, bool override) -> hpp::uuid
Adds an asset to the database.
#define APPLOG_TRACE(...)
Definition logging.h:17
load_mode
Controls whether an asset is loaded immediately or deferred until first access.
Definition asset_flags.h:17
@ deferred
Register handle only; load triggered on first .get() call.
Thread-safe handle to an asset.
struct unravel::animation_importer_meta::root_motion_meta root_motion
Metadata information for an asset including its location.
asset_meta meta
Metadata of the asset.
std::string location
Location of the asset.
Base class for per-type asset header information.
virtual ~asset_header_info()=default
virtual ~asset_importer_meta()=default
Metadata for an asset, including its UUID and type.
std::shared_ptr< asset_header_info > header
Per-type header info (editor only, populated during compilation).
std::shared_ptr< asset_importer_meta > importer
Importer meta.
std::string type
Type of the asset.
hpp::uuid uid
Unique identifier for the asset.
Manages storage and loading of assets of a specific type.
void unload_with_condition(tpp::thread_pool &pool, const predicate_t &predicate)
Unloads assets that satisfy a condition.
load_from_instance_t load_from_instance
Function for loading assets from instance.
callable< bool(tpp::thread_pool &pool, asset_handle< T > &, const std::string &, load_mode)> load_from_file_t
Function type for loading from file.
auto get_group(const std::string &group) const -> std::vector< asset_handle< T > >
Gets all assets in a specified group.
void unload_all(tpp::thread_pool &pool) final
Unloads all assets.
callable< bool(tpp::thread_pool &pool, asset_handle< T > &, std::shared_ptr< T >)> load_from_instance_t
callable< bool(const asset_handle< T > &)> predicate_t
Function type for loading from instance. Predicate function type.
void unload_handle(tpp::thread_pool &pool, asset_handle< T > &handle)
Unloads a handle.
std::function< F > callable
Type alias for callable functions.
void evict_unused(tpp::thread_pool &pool, const std::string &group, std::chrono::steady_clock::duration max_idle) final
Evicts loaded assets not accessed within the given duration. Assets whose weak_ptr has expired (no ex...
std::unordered_map< std::string, asset_handle< T > > request_container_t
Container for asset requests.
std::recursive_mutex container_mutex
Mutex for container operations.
void preload_all() final
Triggers loading on all deferred handles in this storage. Non-blocking: starts background loads but d...
~asset_storage() override=default
void unload_group(tpp::thread_pool &pool, const std::string &group) final
Unloads all assets in a specified group.
load_from_file_t load_from_file
Function for loading assets from file.
auto get_with_condition(const predicate_t &predicate) const -> std::vector< asset_handle< T > >
Gets assets that satisfy a condition.
void unload_single(tpp::thread_pool &pool, const std::string &key) final
Unloads a single asset by its key.
request_container_t container
Container for asset requests.
Abstract base class for asset storage.
virtual void unload_single(tpp::thread_pool &pool, const std::string &key)=0
Unloads a single asset by its key.
virtual void unload_all(tpp::thread_pool &pool)=0
Unloads all assets.
virtual void preload_all()=0
Triggers loading on all deferred handles in this storage. Non-blocking: starts background loads but d...
virtual ~basic_storage()=default
virtual void unload_group(tpp::thread_pool &pool, const std::string &group)=0
Unloads all assets in a specified group.
virtual void evict_unused(tpp::thread_pool &pool, const std::string &group, std::chrono::steady_clock::duration max_idle)=0
Evicts loaded assets not accessed within the given duration. Demotes them back to deferred state so m...
bool generate_lods
Target error for LOD generation (lower = higher quality, higher = more aggressive)
bool find_invalid_data
Enable automatic LOD generation during compilation.
struct unravel::mesh_importer_meta::rig_meta rig
struct unravel::mesh_importer_meta::model_meta model
struct unravel::mesh_importer_meta::animations_meta animations
struct unravel::mesh_importer_meta::materials_meta materials
struct unravel::texture_importer_meta::quality_meta quality
bool invert_normal_y
Bake OpenGL/DirectX normal Y difference into texels at compile (normal_map type only).
gfx::uniform_handle handle
Definition uniform.cpp:9