Vulkan Schnee 0.0.1
High-performance rendering engine
Loading...
Searching...
No Matches
Asset.h
Go to the documentation of this file.
1#pragma once
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>
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
55 template <typename Key, typename AssetClass>
57 {
59 AssetClass* asset = nullptr;
60 bool success = false;
61 std::string error;
62 uint64_t generation = 0;
63 };
64
65 template <typename Key, typename AssetClass>
66 struct Record
67 {
69
70 explicit Record(Key key) : key(std::move(key))
71 {
73 }
74
76 {
77 readyPromise = std::make_shared<std::promise<Result>>();
78 readyFuture = readyPromise->get_future().share();
80 }
81
82 Key key;
83 std::unique_ptr<AssetClass> asset;
85 uint64_t generation = 0;
86 std::string lastError;
87 std::shared_ptr<std::promise<Result>> readyPromise;
88 std::shared_future<Result> readyFuture;
90 };
91
92 template <typename Key, typename AssetClass>
93 class Ref
94 {
95 public:
98
99 Ref() = default;
100
101 [[nodiscard]] LoadState getState() const
102 {
103 const auto record = record_.lock();
104 return record ? record->state : UNLOADED;
105 }
106
107 [[nodiscard]] bool isLoaded() const
108 {
109 return getState() == LOADED;
110 }
111
112 [[nodiscard]] AssetClass* get() const
113 {
114 const auto record = record_.lock();
115 return record && record->state == LOADED ? record->asset.get() : nullptr;
116 }
117
118 [[nodiscard]] uint64_t getGeneration() const
119 {
120 const auto record = record_.lock();
121 return record ? record->generation : 0;
122 }
123
124 [[nodiscard]] const std::string& getError() const
125 {
126 static const std::string empty;
127 const auto record = record_.lock();
128 return record ? record->lastError : empty;
129 }
130
131 [[nodiscard]] std::shared_future<Result> readyFuture() const
132 {
133 const auto record = record_.lock();
134 return record ? record->readyFuture : expiredFuture();
135 }
136
137 [[nodiscard]] explicit operator bool() const
138 {
139 return !record_.expired();
140 }
141
142 private:
143 friend class AssetManager<Key, AssetClass>;
144
145 explicit Ref(std::shared_ptr<RecordType> record) : record_(std::move(record)) {}
146
147 static std::shared_future<Result> expiredFuture()
148 {
149 static std::shared_future<Result> future = []
150 {
151 std::promise<Result> promise;
152 Result result;
153 result.error = "Asset record expired";
154 promise.set_value(std::move(result));
155 return promise.get_future().share();
156 }();
157 return future;
158 }
159
160 std::weak_ptr<RecordType> record_;
161 };
162
168 {
169 public:
170 explicit AssetBase(bool initializeDefaultEntity = false) :
171 loadingState( initializeDefaultEntity ? LOADING : UNLOADED )
172 , mainRegistry( Engine::Ecs::RegistryManager::get() )
173 , data( initializeDefaultEntity ? mainRegistry.create() : entt::null )
174 {
175 };
176 virtual ~AssetBase();
177
182 [[nodiscard]] CpuLoadingState getLoadingState();
183
187 void requestLoad();
188
192 virtual void unload();
193
198 entt::entity getEntity() const;
199
200 protected:
201 virtual void updateLoadingState()
202 {
203 }
205
206 entt::registry& mainRegistry;
207 entt::entity data;
208 };
209
211 {
213 }
214
220
222 {
224 data = mainRegistry.create();
225 }
226
227 inline void AssetBase::unload()
228 {
229 if (data != entt::null && mainRegistry.valid(data))
230 {
231 mainRegistry.destroy(data);
232 }
233 data = entt::null;
235 }
236
237 inline entt::entity AssetBase::getEntity() const
238 {
239 return data;
240 }
241
249 template <typename Key, typename AssetClass>
251 public:
255
256 AssetManager() = default;
257 virtual ~AssetManager() = default;
258
268 template <typename... Args>
269 void declare( Key path, Args &&... args );
278 void add(Key path, AssetClass* asset);
279
283 RefType getOrCreateRef(const Key& path);
284
288 [[nodiscard]] RefType getRef(const Key& path) const;
289
293 void setAssetLoadingState(const Key& path, LoadState state);
294
298 void resolve(const Key& path, std::unique_ptr<AssetClass> asset);
299
303 void fail(const Key& path, std::string error);
304
308 void unloadAssetPayload(const Key& path);
309
315 bool exists(Key path);
316
325 bool isDeclared(Key path);
326
346 std::optional<AssetClass*> getAsset(const Key& path);
347
354 virtual void clear();
355
364 template<typename Func>
365 void forEachAsset(Func&& func) const {
366 for (const auto& [key, record] : records) {
367 if (record && record->asset != nullptr) {
368 func(key, record->asset.get());
369 }
370 }
371 }
372
373 private:
374 std::shared_ptr<RecordType> getOrCreateRecord(const Key& path);
375 [[nodiscard]] std::shared_ptr<RecordType> getRecord(const Key& path) const;
376 void completeRecord(const std::shared_ptr<RecordType>& record, bool success, std::string error = {});
377 void syncRecordStateFromPayload(const std::shared_ptr<RecordType>& record) const;
378
379 std::unordered_map<Key, std::shared_ptr<RecordType>> records;
380 };
381
382 template<typename Key, typename AssetClass>
383 template<typename... Args>
384 void AssetManager<Key, AssetClass>::declare(Key path, Args&&... args) {
385 auto record = getOrCreateRecord(path);
386 record->asset = std::make_unique<AssetClass>(std::forward<Args>(args)...);
387 ++record->generation;
388 record->resetFuture();
390 if (record->state == LOADED) {
391 completeRecord(record, true);
392 }
393 }
394
395 template<typename Key, typename AssetClass>
396 void AssetManager<Key, AssetClass>::add(Key path, AssetClass *asset) {
397 TRACY_ZONE_SCOPED_NAMED( "Added Asset to Asset Manager" );
398 assert(asset != nullptr); // use declare for that
399 auto record = getOrCreateRecord(path);
400 record->asset.reset(asset);
401 ++record->generation;
402 record->resetFuture();
404 if (record->state == LOADED) {
405 completeRecord(record, true);
406 }
407 }
408
409 template<typename Key, typename AssetClass>
413
414 template<typename Key, typename AssetClass>
416 return RefType(getRecord(path));
417 }
418
419 template<typename Key, typename AssetClass>
421 auto record = getOrCreateRecord(path);
422
423 if ((state == REQUESTED_LOAD || state == LOADING) &&
424 (record->state == UNLOADED || record->state == LOADED || record->state == FAILED))
425 {
426 ++record->generation;
427 record->resetFuture();
428 record->lastError.clear();
429 if (state != LOADED) {
430 record->asset.reset();
431 }
432 }
433
434 record->state = state;
435 if (state == LOADED) {
436 completeRecord(record, true);
437 }
438 }
439
440 template<typename Key, typename AssetClass>
441 void AssetManager<Key, AssetClass>::resolve(const Key& path, std::unique_ptr<AssetClass> asset) {
442 auto record = getOrCreateRecord(path);
443 record->asset = std::move(asset);
444 record->state = LOADED;
445 record->lastError.clear();
446 completeRecord(record, true);
447 }
448
449 template<typename Key, typename AssetClass>
450 void AssetManager<Key, AssetClass>::fail(const Key& path, std::string error) {
451 auto record = getOrCreateRecord(path);
452 record->asset.reset();
453 record->state = FAILED;
454 record->lastError = std::move(error);
455 completeRecord(record, false, record->lastError);
456 }
457
458 template<typename Key, typename AssetClass>
460 auto record = getRecord(path);
461 if (!record) return;
462 record->asset.reset();
463 record->state = UNLOADED;
464 ++record->generation;
465 record->resetFuture();
466 record->lastError.clear();
467 }
468
469 template<typename Key, typename AssetClass>
471 const auto record = getRecord(path);
472 return record && record->asset != nullptr;
473 }
474
475 template <typename Key, typename AssetClass>
477 {
478 return records.find(path) != records.end();
479 }
480
481 template <typename Key, typename AssetClass>
483 const auto record = getRecord(path);
484 if (!record) return UNLOADED;
486 return record->state;
487 }
488
489 template<typename Key, typename AssetClass>
490 std::optional<AssetClass *> AssetManager<Key, AssetClass>::getAsset(const Key& path) {
491 TRACY_ZONE_SCOPED_NAMED("Retrieve Asset");
492 const auto record = getRecord(path);
493 if (!record || record->asset == nullptr) return std::nullopt;
495 return record->asset.get();
496 }
497
498 template <typename Key, typename AssetClass>
500 {
501 TRACY_ZONE_SCOPED_NAMED( "Unloading data" );
502 for (auto& [key, record] : records)
503 {
504 TRACY_ZONE_SCOPED_NAMED("Deleting loaded data");
505 if (record) {
506 record->asset.reset();
507 record->state = UNLOADED;
508 ++record->generation;
509 record->resetFuture();
510 record->lastError.clear();
511 }
512 }
513 records.clear();
514 }
515
516 template<typename Key, typename AssetClass>
517 std::shared_ptr<typename AssetManager<Key, AssetClass>::RecordType> AssetManager<Key, AssetClass>::getOrCreateRecord(const Key& path) {
518 auto it = records.find(path);
519 if (it != records.end()) {
520 return it->second;
521 }
522
523 auto record = std::make_shared<RecordType>(path);
524 records.emplace(path, record);
525 return record;
526 }
527
528 template<typename Key, typename AssetClass>
529 std::shared_ptr<typename AssetManager<Key, AssetClass>::RecordType> AssetManager<Key, AssetClass>::getRecord(const Key& path) const {
530 const auto it = records.find(path);
531 return it != records.end() ? it->second : nullptr;
532 }
533
534 template<typename Key, typename AssetClass>
535 void AssetManager<Key, AssetClass>::completeRecord(const std::shared_ptr<RecordType>& record, bool success, std::string error) {
536 if (!record || record->readyPromiseResolved || !record->readyPromise) return;
537
538 ResultType result;
539 result.ref = RefType(record);
540 result.asset = success ? record->asset.get() : nullptr;
541 result.success = success;
542 result.error = std::move(error);
543 result.generation = record->generation;
544
545 record->readyPromise->set_value(std::move(result));
546 record->readyPromiseResolved = true;
547 }
548
549 template<typename Key, typename AssetClass>
550 void AssetManager<Key, AssetClass>::syncRecordStateFromPayload(const std::shared_ptr<RecordType>& record) const {
551 if (!record || record->asset == nullptr) return;
552 if (record->state == FAILED) return;
553
554 const LoadState payloadState = record->asset->getLoadingState();
555 if (payloadState != record->state) {
556 record->state = payloadState;
557 }
558 if (record->state == LOADED) {
559 const_cast<AssetManager*>(this)->completeRecord(record, true);
560 }
561 }
562} // namespace Asset
#define TRACY_ZONE_SCOPED_NAMED(name)
void requestLoad()
If this asset is in the UNLOADED state it will get added to the assets to load.
Definition Asset.h:221
virtual ~AssetBase()
Definition Asset.h:210
virtual void unload()
Base function for unloading the ecs data.
Definition Asset.h:227
entt::entity data
Definition Asset.h:207
entt::entity getEntity() const
Getter for data.
Definition Asset.h:237
AssetBase(bool initializeDefaultEntity=false)
Definition Asset.h:170
virtual void updateLoadingState()
Definition Asset.h:201
CpuLoadingState loadingState
Definition Asset.h:204
CpuLoadingState getLoadingState()
Gets the loading state of this asset.
Definition Asset.h:215
entt::registry & mainRegistry
Definition Asset.h:206
A manager which is used to look up existing assets and their loading state.
Definition Asset.h:250
void declare(Key path, Args &&... args)
Adds an asset to the list in its unloaded state.
Definition Asset.h:384
std::shared_ptr< RecordType > getOrCreateRecord(const Key &path)
Definition Asset.h:517
Record< Key, AssetClass > RecordType
Definition Asset.h:253
LoadResult< Key, AssetClass > ResultType
Definition Asset.h:254
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:482
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:490
void syncRecordStateFromPayload(const std::shared_ptr< RecordType > &record) const
Definition Asset.h:550
void setAssetLoadingState(const Key &path, LoadState state)
Sets the record loading state without requiring a payload object.
Definition Asset.h:420
void fail(const Key &path, std::string error)
Marks the record as failed and resolves the ready future with an error.
Definition Asset.h:450
std::shared_ptr< RecordType > getRecord(const Key &path) const
Definition Asset.h:529
bool isDeclared(Key path)
Checks if an asset has been declared. If it exists it is also declared.
Definition Asset.h:476
void forEachAsset(Func &&func) const
Iterates over all assets and calls the provided callback for each.
Definition Asset.h:365
void completeRecord(const std::shared_ptr< RecordType > &record, bool success, std::string error={})
Definition Asset.h:535
bool exists(Key path)
Checks if an asset already exists. This means it is declared and there is content.
Definition Asset.h:470
AssetManager()=default
virtual void clear()
Clears out all resources.
Definition Asset.h:499
void add(Key path, AssetClass *asset)
Adds or overwrites a previously declared asset at a key.
Definition Asset.h:396
RefType getRef(const Key &path) const
Gets a stable asset reference if a record exists, otherwise an expired ref.
Definition Asset.h:415
virtual ~AssetManager()=default
Ref< Key, AssetClass > RefType
Definition Asset.h:252
void resolve(const Key &path, std::unique_ptr< AssetClass > asset)
Stores a loaded payload and resolves the record's ready future.
Definition Asset.h:441
std::unordered_map< Key, std::shared_ptr< RecordType > > records
Definition Asset.h:379
void unloadAssetPayload(const Key &path)
Drops the loaded payload while preserving the stable record.
Definition Asset.h:459
RefType getOrCreateRef(const Key &path)
Gets a stable asset reference, creating a record if necessary.
Definition Asset.h:410
LoadState getState() const
Definition Asset.h:101
Ref()=default
Ref(std::shared_ptr< RecordType > record)
Definition Asset.h:145
LoadResult< std::filesystem::path, Audio > Result
Definition Asset.h:97
const std::string & getError() const
Definition Asset.h:124
std::shared_future< Result > readyFuture() const
Definition Asset.h:131
std::weak_ptr< RecordType > record_
Definition Asset.h:160
static std::shared_future< Result > expiredFuture()
Definition Asset.h:147
bool isLoaded() const
Definition Asset.h:107
AssetClass * get() const
Definition Asset.h:112
Record< std::filesystem::path, Audio > RecordType
Definition Asset.h:96
uint64_t getGeneration() const
Definition Asset.h:118
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
Definition Asset.h:47
Data structs for the Entity Component System.
STL namespace.
AssetClass * asset
Definition Asset.h:59
Ref< Key, AssetClass > ref
Definition Asset.h:58
std::string error
Definition Asset.h:61
uint64_t generation
Definition Asset.h:62
std::string lastError
Definition Asset.h:86
std::shared_future< Result > readyFuture
Definition Asset.h:88
Record(Key key)
Definition Asset.h:70
void resetFuture()
Definition Asset.h:75
bool readyPromiseResolved
Definition Asset.h:89
uint64_t generation
Definition Asset.h:85
std::shared_ptr< std::promise< Result > > readyPromise
Definition Asset.h:87
LoadState state
Definition Asset.h:84
LoadResult< Key, AssetClass > Result
Definition Asset.h:68
std::unique_ptr< AssetClass > asset
Definition Asset.h:83