#pragma once #include "FileSystem.hpp" #include #include "Logger.h" #include #include #include #include #include #include #include #define INVALID_ASSET_ID UINT32_MAX struct AssetAddress { std::atomic resource; }; struct AssetHandle { size_t slot = 0; uint32_t id = 0; }; class AssetInfo { public: ee_assets::AssetType type = ee_assets::BINARY; std::atomic isReady; AssetAddress* resourceAddress; AssetInfo(void *resource); AssetInfo(const AssetInfo&) = delete; AssetInfo& operator=(const AssetInfo&) = delete; AssetInfo(AssetInfo&& other) noexcept; AssetInfo& operator=(AssetInfo&& other) noexcept; }; struct AssetLoadingEntry { AssetHandle handle; FileHandle* file; IFileSystem* fileOwner; }; struct AssetStreamCommand { enum Type { NONE, LOAD, FIND, GET, COMPLETED } type = NONE; AssetHandle target; uint32_t targetId = INVALID_ASSET_ID; std::filesystem::path targetPath; ee_assets::AssetType targetType = ee_assets::BINARY; std::atomic* out; }; class AssetManager { public: std::atomic isRunning; AssetManager(); ~AssetManager(); void Start(); void Shutdown(); void LoadDir(ee_assets::AssetType type, const std::filesystem::path &path); void LoadAsync(ee_assets::AssetType type, const std::filesystem::path &path); AssetHandle& Find(const std::filesystem::path &path); AssetHandle& Find(const uint32_t &id); template T& FindOnce(const std::filesystem::path &path); template T& FindOnce(const uint32_t &id); void Redirect(AssetHandle &handle, void *resource); template T& Get(const AssetHandle &handle); void Free(AssetHandle &handle); private: std::stop_token *stoken; std::jthread streamingThread; boost::lockfree::spsc_queue> commands; IFileSystem *diskSystem; std::vector activeAssets; std::unordered_map assetsIdMapping; std::unordered_map assetsMapping; std::vector freeAssetSlots; std::vector loadingEntries; std::vector binaryStorage; std::vector savesStorage; std::vector mapMeshesStorage; std::vector shadersStorage; std::vector materialsStorage; void Threading(); void Request(const AssetStreamCommand &command); void Request(const AssetStreamCommand::Type &type); void Loop(); void ProcessCommand(AssetStreamCommand &command); void Release(); template void FinishLoadConstruct(AssetLoadingEntry &loaded, std::vector &storage); template void FinishLoadPlain(AssetLoadingEntry &loaded, std::vector &storage); template void ClearStorage(std::vector &storage); }; template T& AssetManager::Get(const AssetHandle &handle) { std::atomic result; AssetStreamCommand command { .type = AssetStreamCommand::GET, .target = handle, .out = &result }; Request(command); while (command.out->load() == nullptr); return *reinterpret_cast(command.out->load()); } template T& AssetManager::FindOnce(const std::filesystem::path &path) { return Get(Find(path)); } template T& AssetManager::FindOnce(const uint32_t &id) { return Get(Find(id)); } template void AssetManager::FinishLoadConstruct(AssetLoadingEntry &loaded, std::vector &storage) { T* resource = Allocate(loaded.file->data, loaded.file->size); storage.push_back(resource); Redirect(loaded.handle, storage.back()); } template void AssetManager::FinishLoadPlain(AssetLoadingEntry &loaded, std::vector &storage) { T* resource = Allocate(); if (loaded.file->size < sizeof(T)) LOG(CRITICAL, "File size is less than needed for asset of type: %d", typeid(T).name()); std::memcpy(resource, loaded.file->data, sizeof(T)); storage.push_back(resource); Redirect(loaded.handle, storage.back()); } template void AssetManager::ClearStorage(std::vector &storage) { for (T* &resource : storage) { if (resource != nullptr) Deallocate(resource); } storage.clear(); }