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
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
185 Ref() = default;
186
191 [[nodiscard]] LoadState getState() const
192 {
193 const auto record = record_.lock();
194 return record ? record->state : UNLOADED;
195 }
196
201 [[nodiscard]] bool isLoaded() const
202 {
203 return getState() == LOADED;
204 }
205
210 [[nodiscard]] AssetClass* get() const
211 {
212 const auto record = record_.lock();
213 return record && record->state == LOADED ? record->asset.get() : nullptr;
214 }
215
220 [[nodiscard]] uint64_t getGeneration() const
221 {
222 const auto record = record_.lock();
223 return record ? record->generation : 0;
224 }
225
230 [[nodiscard]] const std::string& getError() const
231 {
232 static const std::string empty;
233 const auto record = record_.lock();
234 return record ? record->lastError : empty;
235 }
236
241 [[nodiscard]] std::shared_future<Result> readyFuture() const
242 {
243 const auto record = record_.lock();
244 return record ? record->readyFuture : expiredFuture();
245 }
246
251 [[nodiscard]] explicit operator bool() const
252 {
253 return !record_.expired();
254 }
255
256 private:
257 friend class AssetManager<Key, AssetClass>;
258
259 explicit Ref(std::shared_ptr<RecordType> record) : record_(std::move(record)) {}
260
261 static std::shared_future<Result> expiredFuture()
262 {
263 static std::shared_future<Result> future = []
264 {
265 std::promise<Result> promise;
266 Result result;
267 result.error = "Asset record expired";
268 promise.set_value(std::move(result));
269 return promise.get_future().share();
270 }();
271 return future;
272 }
273
274 std::weak_ptr<RecordType> record_;
275 };
276
282 {
283 public:
284 explicit AssetBase(bool initializeDefaultEntity = false) :
285 loadingState( initializeDefaultEntity ? LOADING : UNLOADED )
286 , mainRegistry( Engine::Ecs::RegistryManager::get() )
287 , data( initializeDefaultEntity ? mainRegistry.create() : entt::null )
288 {
289 };
290 virtual ~AssetBase();
291
297
302
306 virtual void unload();
307
312 entt::entity getEntity() const;
313
314 protected:
315 virtual void updateLoadingState()
316 {
317 }
319
320 entt::registry& mainRegistry;
321 entt::entity data;
322 };
323
331 template <typename Key, typename AssetClass>
333 public:
337
338 AssetManager() = default;
339 virtual ~AssetManager() = default;
340
347 template <typename... Args>
348 void declare( Key path, Args &&... args );
349
355 void add(Key path, AssetClass* asset);
356
360 RefType getOrCreateRef(const Key& path);
361
365 [[nodiscard]] RefType getRef(const Key& path) const;
366
370 void setAssetLoadingState(const Key& path, LoadState state);
371
375 void resolve(const Key& path, std::unique_ptr<AssetClass> asset);
376
380 void fail(const Key& path, std::string error);
381
385 void unloadAssetPayload(const Key& path);
386
392 bool exists(Key path);
393
399 bool isDeclared(Key path);
400
414 std::optional<AssetClass*> getAsset(const Key& path);
415
419 virtual void clear();
420
426 template<typename Func>
427 void forEachAsset(Func&& func) const {
428 for (const auto& [key, record] : records) {
429 if (record && record->asset != nullptr) {
430 func(key, record->asset.get());
431 }
432 }
433 }
434
435 private:
436 std::shared_ptr<RecordType> getOrCreateRecord(const Key& path);
437 [[nodiscard]] std::shared_ptr<RecordType> getRecord(const Key& path) const;
438 void completeRecord(const std::shared_ptr<RecordType>& record, bool success, std::string error = {});
439 void syncRecordStateFromPayload(const std::shared_ptr<RecordType>& record) const;
440
441 std::unordered_map<Key, std::shared_ptr<RecordType>> records;
442 };
443
444 template<typename Key, typename AssetClass>
445 template<typename... Args>
446 void AssetManager<Key, AssetClass>::declare(Key path, Args&&... args) {
447 auto record = getOrCreateRecord(path);
448 record->asset = std::make_unique<AssetClass>(std::forward<Args>(args)...);
449 ++record->generation;
450 record->resetFuture();
452 if (record->state == LOADED) {
453 completeRecord(record, true);
454 }
455 }
456
457 template<typename Key, typename AssetClass>
458 void AssetManager<Key, AssetClass>::add(Key path, AssetClass *asset) {
459 TRACY_ZONE_SCOPED_NAMED( "Added Asset to Asset Manager" );
460 assert(asset != nullptr); // use declare for that
461 auto record = getOrCreateRecord(path);
462 record->asset.reset(asset);
463 ++record->generation;
464 record->resetFuture();
466 if (record->state == LOADED) {
467 completeRecord(record, true);
468 }
469 }
470
471 template<typename Key, typename AssetClass>
475
476 template<typename Key, typename AssetClass>
478 return RefType(getRecord(path));
479 }
480
481 template<typename Key, typename AssetClass>
483 auto record = getOrCreateRecord(path);
484
485 if ((state == REQUESTED_LOAD || state == LOADING) &&
486 (record->state == UNLOADED || record->state == LOADED || record->state == FAILED))
487 {
488 ++record->generation;
489 record->resetFuture();
490 record->lastError.clear();
491 if (state != LOADED) {
492 record->asset.reset();
493 }
494 }
495
496 record->state = state;
497 if (state == LOADED) {
498 completeRecord(record, true);
499 }
500 }
501
502 template<typename Key, typename AssetClass>
503 void AssetManager<Key, AssetClass>::resolve(const Key& path, std::unique_ptr<AssetClass> asset) {
504 auto record = getOrCreateRecord(path);
505 record->asset = std::move(asset);
506 record->state = LOADED;
507 record->lastError.clear();
508 completeRecord(record, true);
509 }
510
511 template<typename Key, typename AssetClass>
512 void AssetManager<Key, AssetClass>::fail(const Key& path, std::string error) {
513 auto record = getOrCreateRecord(path);
514 record->asset.reset();
515 record->state = FAILED;
516 record->lastError = std::move(error);
517 completeRecord(record, false, record->lastError);
518 }
519
520 template<typename Key, typename AssetClass>
522 auto record = getRecord(path);
523 if (!record) return;
524 record->asset.reset();
525 record->state = UNLOADED;
526 ++record->generation;
527 record->resetFuture();
528 record->lastError.clear();
529 }
530
531 template<typename Key, typename AssetClass>
533 const auto record = getRecord(path);
534 return record && record->asset != nullptr;
535 }
536
537 template <typename Key, typename AssetClass>
539 {
540 return records.find(path) != records.end();
541 }
542
543 template <typename Key, typename AssetClass>
545 const auto record = getRecord(path);
546 if (!record) return UNLOADED;
548 return record->state;
549 }
550
551 template<typename Key, typename AssetClass>
552 std::optional<AssetClass *> AssetManager<Key, AssetClass>::getAsset(const Key& path) {
553 TRACY_ZONE_SCOPED_NAMED("Retrieve Asset");
554 const auto record = getRecord(path);
555 if (!record || record->asset == nullptr) return std::nullopt;
557 return record->asset.get();
558 }
559
560 template <typename Key, typename AssetClass>
562 {
563 TRACY_ZONE_SCOPED_NAMED( "Unloading data" );
564 for (auto& [key, record] : records)
565 {
566 TRACY_ZONE_SCOPED_NAMED("Deleting loaded data");
567 if (record) {
568 record->asset.reset();
569 record->state = UNLOADED;
570 ++record->generation;
571 record->resetFuture();
572 record->lastError.clear();
573 }
574 }
575 records.clear();
576 }
577
578 template<typename Key, typename AssetClass>
579 std::shared_ptr<typename AssetManager<Key, AssetClass>::RecordType> AssetManager<Key, AssetClass>::getOrCreateRecord(const Key& path) {
580 auto it = records.find(path);
581 if (it != records.end()) {
582 return it->second;
583 }
584
585 auto record = std::make_shared<RecordType>(path);
586 records.emplace(path, record);
587 return record;
588 }
589
590 template<typename Key, typename AssetClass>
591 std::shared_ptr<typename AssetManager<Key, AssetClass>::RecordType> AssetManager<Key, AssetClass>::getRecord(const Key& path) const {
592 const auto it = records.find(path);
593 return it != records.end() ? it->second : nullptr;
594 }
595
596 template<typename Key, typename AssetClass>
597 void AssetManager<Key, AssetClass>::completeRecord(const std::shared_ptr<RecordType>& record, bool success, std::string error) {
598 if (!record || record->readyPromiseResolved || !record->readyPromise) return;
599
600 ResultType result;
601 result.ref = RefType(record);
602 result.asset = success ? record->asset.get() : nullptr;
603 result.success = success;
604 result.error = std::move(error);
605 result.generation = record->generation;
606
607 record->readyPromise->set_value(std::move(result));
608 record->readyPromiseResolved = true;
609 }
610
611 template<typename Key, typename AssetClass>
612 void AssetManager<Key, AssetClass>::syncRecordStateFromPayload(const std::shared_ptr<RecordType>& record) const {
613 if (!record || record->asset == nullptr) return;
614 if (record->state == FAILED) return;
615
616 const LoadState payloadState = record->asset->getLoadingState();
617 if (payloadState != record->state) {
618 record->state = payloadState;
619 }
620 if (record->state == LOADED) {
621 const_cast<AssetManager*>(this)->completeRecord(record, true);
622 }
623 }
624} // 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.
entt::entity data
Definition Asset.h:321
entt::entity getEntity() const
Getter for data.
virtual ~AssetBase()
virtual void unload()
Base function for unloading the ecs data.
AssetBase(bool initializeDefaultEntity=false)
Definition Asset.h:284
virtual void updateLoadingState()
Definition Asset.h:315
CpuLoadingState loadingState
Definition Asset.h:318
CpuLoadingState getLoadingState()
Gets the loading state of this asset.
entt::registry & mainRegistry
Definition Asset.h:320
A manager which is used to look up existing assets and their loading state.
Definition Asset.h:332
void declare(Key path, Args &&... args)
Adds an asset to the list in its unloaded state.
Definition Asset.h:446
std::shared_ptr< RecordType > getOrCreateRecord(const Key &path)
Definition Asset.h:579
Record< Key, AssetClass > RecordType
Definition Asset.h:335
LoadResult< Key, AssetClass > ResultType
Definition Asset.h:336
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:544
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:552
void syncRecordStateFromPayload(const std::shared_ptr< RecordType > &record) const
Definition Asset.h:612
void setAssetLoadingState(const Key &path, LoadState state)
Sets the record loading state without requiring a payload object.
Definition Asset.h:482
void fail(const Key &path, std::string error)
Marks the record as failed and resolves the ready future with an error.
Definition Asset.h:512
std::shared_ptr< RecordType > getRecord(const Key &path) const
Definition Asset.h:591
bool isDeclared(Key path)
Checks if an asset has been declared. If it exists it is also declared.
Definition Asset.h:538
void forEachAsset(Func &&func) const
Iterates over all assets and calls the provided callback for each.
Definition Asset.h:427
void completeRecord(const std::shared_ptr< RecordType > &record, bool success, std::string error={})
Definition Asset.h:597
bool exists(Key path)
Checks if an asset already exists. This means it is declared and there is content.
Definition Asset.h:532
AssetManager()=default
virtual void clear()
Clears out all resources.
Definition Asset.h:561
void add(Key path, AssetClass *asset)
Adds or overwrites a previously declared asset at a key.
Definition Asset.h:458
RefType getRef(const Key &path) const
Gets a stable asset reference if a record exists, otherwise an expired ref.
Definition Asset.h:477
virtual ~AssetManager()=default
Ref< Key, AssetClass > RefType
Definition Asset.h:334
void resolve(const Key &path, std::unique_ptr< AssetClass > asset)
Stores a loaded payload and resolves the record's ready future.
Definition Asset.h:503
std::unordered_map< Key, std::shared_ptr< RecordType > > records
Definition Asset.h:441
void unloadAssetPayload(const Key &path)
Drops the loaded payload while preserving the stable record.
Definition Asset.h:521
RefType getOrCreateRef(const Key &path)
Gets a stable asset reference, creating a record if necessary.
Definition Asset.h:472
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:191
Ref()=default
Ref(std::shared_ptr< RecordType > record)
Definition Asset.h:259
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:230
std::shared_future< Result > readyFuture() const
Gets a future for completion of the current generation.
Definition Asset.h:241
std::weak_ptr< RecordType > record_
Definition Asset.h:274
static std::shared_future< Result > expiredFuture()
Definition Asset.h:261
bool isLoaded() const
Checks whether the referenced asset has a loaded payload.
Definition Asset.h:201
AssetClass * get() const
Gets the loaded payload.
Definition Asset.h:210
Record< Asset::MeshPath, Animation > RecordType
Definition Asset.h:182
uint64_t getGeneration() const
Gets the current record generation.
Definition Asset.h:220
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.
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
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