Vulkan Schnee 0.0.1 (22840dc8)
High-performance rendering engine
Loading...
Searching...
No Matches
Asset.h
1#pragma once
2#include "Engine/Logging/TracyMacros.hpp"
3#include <optional>
4#include <cstdint>
5#include <entt/entt.hpp>
6#include <future>
7#include <memory>
8#include <string>
9#include <utility>
10#include <unordered_map>
11#include <Engine/Ecs/RegistryManager.h>
12
17namespace Asset
18{
22 enum CpuLoadingState : uint8_t {
40 LOADED = 3,
45 };
46
48
49 template <typename Key, typename AssetClass>
50 class AssetManager;
51
52 template <typename Key, typename AssetClass>
53 class Ref;
54
64 template <typename Key, typename AssetClass>
66 {
71
75 AssetClass* asset = nullptr;
76
80 bool success = false;
81
85 std::string error;
86
90 uint64_t generation = 0;
91 };
92
103 template <typename Key, typename AssetClass>
104 struct Record
105 {
107
112 explicit Record(Key key) : key(std::move(key))
113 {
114 resetFuture();
115 }
116
121 {
122 readyPromise = std::make_shared<std::promise<Result>>();
123 readyFuture = readyPromise->get_future().share();
124 readyPromiseResolved = false;
125 }
126
130 Key key;
131
135 std::unique_ptr<AssetClass> asset;
136
141
145 uint64_t generation = 0;
146
150 std::string lastError;
151
155 std::shared_ptr<std::promise<Result>> readyPromise;
156
160 std::shared_future<Result> readyFuture;
161
166 };
167
178 template <typename Key, typename AssetClass>
179 class Ref
180 {
181 public:
184
188 Ref() = default;
189
194 [[nodiscard]] LoadState getState() const
195 {
196 const auto record = record_.lock();
197 return record ? record->state : UNLOADED;
198 }
199
204 [[nodiscard]] bool isLoaded() const
205 {
206 return getState() == LOADED;
207 }
208
213 [[nodiscard]] AssetClass* get() const
214 {
215 const auto record = record_.lock();
216 return record && record->state == LOADED ? record->asset.get() : nullptr;
217 }
218
226 [[nodiscard]] std::shared_ptr<RecordType> sharedRecord() const
227 {
228 return record_.lock();
229 }
230
235 [[nodiscard]] uint64_t getGeneration() const
236 {
237 const auto record = record_.lock();
238 return record ? record->generation : 0;
239 }
240
245 [[nodiscard]] const std::string& getError() const
246 {
247 static const std::string empty;
248 const auto record = record_.lock();
249 return record ? record->lastError : empty;
250 }
251
256 [[nodiscard]] std::shared_future<Result> readyFuture() const
257 {
258 const auto record = record_.lock();
259 return record ? record->readyFuture : expiredFuture();
260 }
261
266 [[nodiscard]] explicit operator bool() const
267 {
268 return !record_.expired();
269 }
270
271 private:
272 friend class AssetManager<Key, AssetClass>;
273
274 explicit Ref(std::shared_ptr<RecordType> record) : record_(std::move(record)) {}
275
276 static std::shared_future<Result> expiredFuture()
277 {
278 static std::shared_future<Result> future = []
279 {
280 std::promise<Result> promise;
281 Result result;
282 result.error = "Asset record expired";
283 promise.set_value(std::move(result));
284 return promise.get_future().share();
285 }();
286 return future;
287 }
288
289 std::weak_ptr<RecordType> record_;
290 };
291
297 {
298 public:
303 explicit AssetBase(bool initializeDefaultEntity = false) :
304 loadingState( initializeDefaultEntity ? LOADING : UNLOADED )
305 , mainRegistry( Engine::Ecs::RegistryManager::get() )
306 , data( initializeDefaultEntity ? mainRegistry.create() : entt::null )
307 {
308 };
309
313 virtual ~AssetBase();
314
320
325
329 virtual void unload();
330
335 entt::entity getEntity() const;
336
337 protected:
341 virtual void updateLoadingState()
342 {
343 }
345
346 entt::registry& mainRegistry;
347 entt::entity data;
348 };
349
357 template <typename Key, typename AssetClass>
359 public:
363
367 AssetManager() = default;
368
372 virtual ~AssetManager() = default;
373
380 template <typename... Args>
381 void declare( Key path, Args &&... args );
382
388 void add(Key path, AssetClass* asset);
389
393 RefType getOrCreateRef(const Key& path);
394
398 [[nodiscard]] RefType getRef(const Key& path) const;
399
403 void setAssetLoadingState(const Key& path, LoadState state);
404
408 void resolve(const Key& path, std::unique_ptr<AssetClass> asset);
409
413 void fail(const Key& path, std::string error);
414
418 void unloadAssetPayload(const Key& path);
419
425 bool exists(Key path);
426
432 bool isDeclared(Key path);
433
447 std::optional<AssetClass*> getAsset(const Key& path);
448
452 virtual void clear();
453
459 template<typename Func>
460 void forEachAsset(Func&& func) const {
461 for (const auto& [key, record] : records) {
462 if (record && record->asset != nullptr) {
463 func(key, record->asset.get());
464 }
465 }
466 }
467
468 private:
469 std::shared_ptr<RecordType> getOrCreateRecord(const Key& path);
470 [[nodiscard]] std::shared_ptr<RecordType> getRecord(const Key& path) const;
471 void completeRecord(const std::shared_ptr<RecordType>& record, bool success, std::string error = {});
472 void syncRecordStateFromPayload(const std::shared_ptr<RecordType>& record) const;
473
474 std::unordered_map<Key, std::shared_ptr<RecordType>> records;
475 };
476
477 template<typename Key, typename AssetClass>
478 template<typename... Args>
479 void AssetManager<Key, AssetClass>::declare(Key path, Args&&... args) {
480 auto record = getOrCreateRecord(path);
481 record->asset = std::make_unique<AssetClass>(std::forward<Args>(args)...);
482 ++record->generation;
483 record->resetFuture();
485 if (record->state == LOADED) {
486 completeRecord(record, true);
487 }
488 }
489
490 template<typename Key, typename AssetClass>
491 void AssetManager<Key, AssetClass>::add(Key path, AssetClass *asset) {
492 TRACY_ZONE_SCOPED_NAMED( "Added Asset to Asset Manager" );
493 assert(asset != nullptr); // use declare for that
494 auto record = getOrCreateRecord(path);
495 record->asset.reset(asset);
496 ++record->generation;
497 record->resetFuture();
499 if (record->state == LOADED) {
500 completeRecord(record, true);
501 }
502 }
503
504 template<typename Key, typename AssetClass>
508
509 template<typename Key, typename AssetClass>
511 return RefType(getRecord(path));
512 }
513
514 template<typename Key, typename AssetClass>
516 auto record = getOrCreateRecord(path);
517
518 if ((state == REQUESTED_LOAD || state == LOADING) &&
519 (record->state == UNLOADED || record->state == LOADED || record->state == FAILED))
520 {
521 ++record->generation;
522 record->resetFuture();
523 record->lastError.clear();
524 if (state != LOADED) {
525 record->asset.reset();
526 }
527 }
528
529 record->state = state;
530 if (state == LOADED) {
531 completeRecord(record, true);
532 }
533 }
534
535 template<typename Key, typename AssetClass>
536 void AssetManager<Key, AssetClass>::resolve(const Key& path, std::unique_ptr<AssetClass> asset) {
537 auto record = getOrCreateRecord(path);
538 record->asset = std::move(asset);
539 record->state = LOADED;
540 record->lastError.clear();
541 completeRecord(record, true);
542 }
543
544 template<typename Key, typename AssetClass>
545 void AssetManager<Key, AssetClass>::fail(const Key& path, std::string error) {
546 auto record = getOrCreateRecord(path);
547 record->asset.reset();
548 record->state = FAILED;
549 record->lastError = std::move(error);
550 completeRecord(record, false, record->lastError);
551 }
552
553 template<typename Key, typename AssetClass>
555 auto record = getRecord(path);
556 if (!record) return;
557 record->asset.reset();
558 record->state = UNLOADED;
559 ++record->generation;
560 record->resetFuture();
561 record->lastError.clear();
562 }
563
564 template<typename Key, typename AssetClass>
566 const auto record = getRecord(path);
567 return record && record->asset != nullptr;
568 }
569
570 template <typename Key, typename AssetClass>
572 {
573 return records.find(path) != records.end();
574 }
575
576 template <typename Key, typename AssetClass>
578 const auto record = getRecord(path);
579 if (!record) return UNLOADED;
581 return record->state;
582 }
583
584 template<typename Key, typename AssetClass>
585 std::optional<AssetClass *> AssetManager<Key, AssetClass>::getAsset(const Key& path) {
586 TRACY_ZONE_SCOPED_NAMED("Retrieve Asset");
587 const auto record = getRecord(path);
588 if (!record || record->asset == nullptr) return std::nullopt;
590 return record->asset.get();
591 }
592
593 template <typename Key, typename AssetClass>
595 {
596 TRACY_ZONE_SCOPED_NAMED( "Unloading data" );
597 for (auto& [key, record] : records)
598 {
599 TRACY_ZONE_SCOPED_NAMED("Deleting loaded data");
600 if (record) {
601 record->asset.reset();
602 record->state = UNLOADED;
603 ++record->generation;
604 record->resetFuture();
605 record->lastError.clear();
606 }
607 }
608 records.clear();
609 }
610
611 template<typename Key, typename AssetClass>
612 std::shared_ptr<typename AssetManager<Key, AssetClass>::RecordType> AssetManager<Key, AssetClass>::getOrCreateRecord(const Key& path) {
613 auto it = records.find(path);
614 if (it != records.end()) {
615 return it->second;
616 }
617
618 auto record = std::make_shared<RecordType>(path);
619 records.emplace(path, record);
620 return record;
621 }
622
623 template<typename Key, typename AssetClass>
624 std::shared_ptr<typename AssetManager<Key, AssetClass>::RecordType> AssetManager<Key, AssetClass>::getRecord(const Key& path) const {
625 const auto it = records.find(path);
626 return it != records.end() ? it->second : nullptr;
627 }
628
629 template<typename Key, typename AssetClass>
630 void AssetManager<Key, AssetClass>::completeRecord(const std::shared_ptr<RecordType>& record, bool success, std::string error) {
631 if (!record || record->readyPromiseResolved || !record->readyPromise) return;
632
633 ResultType result;
634 result.ref = RefType(record);
635 result.asset = success ? record->asset.get() : nullptr;
636 result.success = success;
637 result.error = std::move(error);
638 result.generation = record->generation;
639
640 record->readyPromise->set_value(std::move(result));
641 record->readyPromiseResolved = true;
642 }
643
644 template<typename Key, typename AssetClass>
645 void AssetManager<Key, AssetClass>::syncRecordStateFromPayload(const std::shared_ptr<RecordType>& record) const {
646 if (!record || record->asset == nullptr) return;
647 if (record->state == FAILED) return;
648
649 const LoadState payloadState = record->asset->getLoadingState();
650 if (payloadState != record->state) {
651 record->state = payloadState;
652 }
653 if (record->state == LOADED) {
654 const_cast<AssetManager*>(this)->completeRecord(record, true);
655 }
656 }
657} // namespace Asset
void requestLoad()
If this asset is in the UNLOADED state it will get added to the assets to load.
entt::entity data
Entity containing the asset's component data.
Definition Asset.h:347
entt::entity getEntity() const
Getter for data.
virtual ~AssetBase()
Releases base asset resources.
virtual void unload()
Base function for unloading the ecs data.
AssetBase(bool initializeDefaultEntity=false)
Creates a base asset and optionally allocates its default ECS entity.
Definition Asset.h:303
virtual void updateLoadingState()
Updates loadingState from derived asset data.
Definition Asset.h:341
CpuLoadingState loadingState
Current CPU loading state.
Definition Asset.h:344
CpuLoadingState getLoadingState()
Gets the loading state of this asset.
entt::registry & mainRegistry
Registry that owns this asset's ECS data.
Definition Asset.h:346
A manager which is used to look up existing assets and their loading state.
Definition Asset.h:358
void declare(Key path, Args &&... args)
Adds an asset to the list in its unloaded state.
Definition Asset.h:479
std::shared_ptr< RecordType > getOrCreateRecord(const Key &path)
Definition Asset.h:612
Record< Key, AssetClass > RecordType
Internal record type stored by this manager.
Definition Asset.h:361
LoadResult< Key, AssetClass > ResultType
Completion result type for async loads.
Definition Asset.h:362
CpuLoadingState getAssetLoadingState(const Key &path)
Gets a loading state for an asset. If the asset is not yet declared it will return that it is UNLOADE...
Definition Asset.h:577
std::optional< AssetClass * > getAsset(const Key &path)
Try's to get an asset. If the asset does not exist (can be checked with exists) it will return a std:...
Definition Asset.h:585
void syncRecordStateFromPayload(const std::shared_ptr< RecordType > &record) const
Definition Asset.h:645
void setAssetLoadingState(const Key &path, LoadState state)
Sets the record loading state without requiring a payload object.
Definition Asset.h:515
void fail(const Key &path, std::string error)
Marks the record as failed and resolves the ready future with an error.
Definition Asset.h:545
std::shared_ptr< RecordType > getRecord(const Key &path) const
Definition Asset.h:624
bool isDeclared(Key path)
Checks if an asset has been declared. If it exists it is also declared.
Definition Asset.h:571
void forEachAsset(Func &&func) const
Iterates over all assets and calls the provided callback for each.
Definition Asset.h:460
void completeRecord(const std::shared_ptr< RecordType > &record, bool success, std::string error={})
Definition Asset.h:630
bool exists(Key path)
Checks if an asset already exists. This means it is declared and there is content.
Definition Asset.h:565
AssetManager()=default
Creates an empty asset manager.
virtual void clear()
Clears out all resources.
Definition Asset.h:594
void add(Key path, AssetClass *asset)
Adds or overwrites a previously declared asset at a key.
Definition Asset.h:491
RefType getRef(const Key &path) const
Gets a stable asset reference if a record exists, otherwise an expired ref.
Definition Asset.h:510
virtual ~AssetManager()=default
Destroys the manager object.
Ref< Key, AssetClass > RefType
Stable reference type returned by this manager.
Definition Asset.h:360
void resolve(const Key &path, std::unique_ptr< AssetClass > asset)
Stores a loaded payload and resolves the record's ready future.
Definition Asset.h:536
std::unordered_map< Key, std::shared_ptr< RecordType > > records
Definition Asset.h:474
void unloadAssetPayload(const Key &path)
Drops the loaded payload while preserving the stable record.
Definition Asset.h:554
RefType getOrCreateRef(const Key &path)
Gets a stable asset reference, creating a record if necessary.
Definition Asset.h:505
Non-owning handle to an asset record managed by AssetManager.
Definition Asset.h:180
LoadState getState() const
Gets the current loading state.
Definition Asset.h:194
Ref()=default
Creates an empty ref that points to no record.
std::shared_ptr< RecordType > sharedRecord() const
Gets a shared handle to the underlying record, keeping it (and its asset payload) alive as long as an...
Definition Asset.h:226
Ref(std::shared_ptr< RecordType > record)
Definition Asset.h:274
LoadResult< Asset::MeshPath, Animation > Result
Definition Asset.h:183
const std::string & getError() const
Gets the last load error reported by the record.
Definition Asset.h:245
std::shared_future< Result > readyFuture() const
Gets a future for completion of the current generation.
Definition Asset.h:256
std::weak_ptr< RecordType > record_
Definition Asset.h:289
static std::shared_future< Result > expiredFuture()
Definition Asset.h:276
bool isLoaded() const
Checks whether the referenced asset has a loaded payload.
Definition Asset.h:204
AssetClass * get() const
Gets the loaded payload.
Definition Asset.h:213
Record< Asset::MeshPath, Animation > RecordType
Definition Asset.h:182
uint64_t getGeneration() const
Gets the current record generation.
Definition Asset.h:235
Classes which are related to asset loading are mostly stored in this namespace.
CpuLoadingState
State for assets in the asset loading process.
Definition Asset.h:22
@ LOADED
All components of the asset have been loaded, and it is ready to be used.
Definition Asset.h:40
@ REQUESTED_LOAD
An asset has been requested to load and is waiting for an asset pipeline to start working on it.
Definition Asset.h:32
@ LOADING
While the asset is currently being processed in the asset pipeline.
Definition Asset.h:36
@ UNLOADED
When the asset is not loaded yet but the frame is already existing for the heavy data to be loaded in...
Definition Asset.h:27
@ FAILED
Asset loading finished unsuccessfully. Inspect the asset ref error for details.
Definition Asset.h:44
CpuLoadingState LoadState
Alias used by the asset manager and refs.
Definition Asset.h:47
STL namespace.
Result returned when an asynchronous asset load reaches a terminal state.
Definition Asset.h:66
AssetClass * asset
Loaded asset payload, or nullptr when loading failed or the record expired.
Definition Asset.h:75
bool success
True when the payload finished loading successfully.
Definition Asset.h:80
Ref< Key, AssetClass > ref
Stable reference to the record that produced this result.
Definition Asset.h:70
std::string error
Failure message for unsuccessful loads.
Definition Asset.h:85
uint64_t generation
Record generation that completed.
Definition Asset.h:90
Internal storage for one asset entry managed by AssetManager.
Definition Asset.h:105
std::string lastError
Last failure message reported for this record.
Definition Asset.h:150
std::shared_future< Result > readyFuture
Shared future resolved when the current generation succeeds or fails.
Definition Asset.h:160
Record(Key key)
Creates an unloaded record for an asset key.
Definition Asset.h:112
void resetFuture()
Starts a new ready future for the current generation.
Definition Asset.h:120
bool readyPromiseResolved
True after readyPromise has been resolved for the current generation.
Definition Asset.h:165
uint64_t generation
Monotonic version incremented when the payload lifecycle restarts.
Definition Asset.h:145
std::shared_ptr< std::promise< Result > > readyPromise
Promise used by AssetManager to complete readyFuture.
Definition Asset.h:155
LoadState state
Current CPU loading state for the record.
Definition Asset.h:140
LoadResult< Key, AssetClass > Result
Completion result type for this record.
Definition Asset.h:106
Key key
Asset lookup key for this record.
Definition Asset.h:130
std::unique_ptr< AssetClass > asset
Owned payload instance, or nullptr while no payload is available.
Definition Asset.h:135