From b61642e4e80a463c4c8085e0c6f46d3e9417e8fc Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 24 Nov 2015 10:37:58 +0100 Subject: [PATCH] Added Resource Manager --- include/Engine/Core/ResourceManager.h | 148 ++++++++++++++++++++++++++ src/Engine/Core/ResourceManager.cpp | 118 ++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 include/Engine/Core/ResourceManager.h create mode 100644 src/Engine/Core/ResourceManager.cpp diff --git a/include/Engine/Core/ResourceManager.h b/include/Engine/Core/ResourceManager.h new file mode 100644 index 00000000..3a54ffb2 --- /dev/null +++ b/include/Engine/Core/ResourceManager.h @@ -0,0 +1,148 @@ +#ifndef ResourceManager_h__ +#define ResourceManager_h__ + +#include +#include +#include +#include +#include +#include + +#include "Util/UnorderedMapPair.h" +#include "Util/Factory.h" +#include "Util/FileWatcher.h" + +/** Base Resource class. + + Implement this class for every resource to be handled by the resource manager. + Implement Create() to return a new object of that type. +*/ +class Resource +{ + friend class ResourceManager; + +protected: + Resource() { } + +public: + // Pretend that this is a pure virtual function that you have to implement + // FIXME: Why did we do this again instead of just using the constructor? + // static Resource* Create(std::string resourceName); + + virtual void Reload() { } + virtual void OnChildReloaded(Resource* child) { } + + unsigned int TypeID; + unsigned int ResourceID; +}; + +/** Singleton resource manager to keep track of and cache any external engine assets */ +class ResourceManager +{ +private: + ResourceManager(); + +public: + /*static ResourceManager& Instance() + { + static ResourceManager s; + return s; + }*/ + + template + static void RegisterType(std::string typeName); + + /** Preloads a resource and caches it for future use + + @tparam T Resource type. + @param resourceName Fully qualified name of the resource to preload. + */ + template + static void Preload(std::string resourceName); + static void Preload(std::string resourceType, std::string resourceName); + + /** Checks if a resource is in cache + + @param resourceType Resource type as string. + @param resourceName Fully qualified name of the resource to preload. + */ + // TODO: Templateify + static bool IsResourceLoaded(std::string resourceType, std::string resourceName); + + /** Hot-loads a resource and caches it for future use + + @tparam T Resource type. + @param resourceName Fully qualified name of the resource to load. + */ + template + static T* Load(std::string resourceName, Resource* parent = nullptr); + static Resource* Load(std::string resourceType, std::string resourceName, Resource* parent = nullptr); + + /** Reloads an already loaded resource, keeping its resource ID intact. + + @tparam T Resource type. + @param resourceName Fully qualified name of the resource to reload. + */ + static void Reload(std::string resourceName); + + static void Update(); + +private: + static std::unordered_map m_CompilerTypenameToResourceType; + static std::unordered_map> m_FactoryFunctions; // type -> factory function + static std::unordered_map, Resource*> m_ResourceCache; // (type, name) -> resource + static std::unordered_map m_ResourceFromName; // name -> resource + static std::unordered_map m_ResourceParents; // resource -> parent resource + + // TODO: Getters for IDs + static unsigned int m_CurrentResourceTypeID; + static std::unordered_map m_ResourceTypeIDs; + // Number of resources of a type. Doubles as local ID. + static std::unordered_map m_ResourceCount; + // Flag to suppress hot-load warnings when a preloading resource chain loads another resource + static bool m_Preloading; + + static FileWatcher m_FileWatcher; + static void fileWatcherCallback(std::string path, FileWatcher::FileEventFlags flags); + + static unsigned int GetTypeID(std::string resourceType); + static unsigned int GetNewResourceID(unsigned int typeID); + + // Internal: Create a resource and cache it + static Resource* CreateResource(std::string resourceType, std::string resourceName, Resource* parent); +}; + +template +T* ResourceManager::Load(std::string resourceName, Resource* parent /* = nullptr */) +{ + auto resourceTypename = typeid(T).name(); + auto it = m_CompilerTypenameToResourceType.find(resourceTypename); + if (it == m_CompilerTypenameToResourceType.end()) { + LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); + return nullptr; + } + + return static_cast(Load(it->second, resourceName, parent)); +} + +template +void ResourceManager::RegisterType(std::string typeName) +{ + m_CompilerTypenameToResourceType[typeid(T).name()] = typeName; + m_FactoryFunctions[typeName] = [](std::string resourceName) { return new T(resourceName); }; +} + +template +void ResourceManager::Preload(std::string resourceName) +{ + auto resourceTypename = typeid(T).name(); + auto it = m_CompilerTypenameToResourceType.find(resourceTypename); + if (it == m_CompilerTypenameToResourceType.end()) { + LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); + return; + } + + Preload(it->second, resourceName); +} + +#endif diff --git a/src/Engine/Core/ResourceManager.cpp b/src/Engine/Core/ResourceManager.cpp new file mode 100644 index 00000000..908b341d --- /dev/null +++ b/src/Engine/Core/ResourceManager.cpp @@ -0,0 +1,118 @@ +#include "PrecompiledHeader.h" +#include "Core/ResourceManager.h" + +std::unordered_map ResourceManager::m_CompilerTypenameToResourceType; +std::unordered_map> ResourceManager::m_FactoryFunctions; +std::unordered_map, Resource*> ResourceManager::m_ResourceCache; +std::unordered_map ResourceManager::m_ResourceFromName; +std::unordered_map ResourceManager::m_ResourceParents; +unsigned int ResourceManager::m_CurrentResourceTypeID = 0; +std::unordered_map ResourceManager::m_ResourceTypeIDs; +std::unordered_map ResourceManager::m_ResourceCount; +bool ResourceManager::m_Preloading = false; +FileWatcher ResourceManager::m_FileWatcher; + +unsigned int ResourceManager::GetTypeID(std::string resourceType) +{ + if (m_ResourceTypeIDs.find(resourceType) == m_ResourceTypeIDs.end()) { + m_ResourceTypeIDs[resourceType] = m_CurrentResourceTypeID++; + } + return m_ResourceTypeIDs[resourceType]; +} + + +void ResourceManager::Reload(std::string resourceName) +{ + auto it = m_ResourceFromName.find(resourceName); + if (it != m_ResourceFromName.end()) { + LOG_INFO("Reloading resource \"%s\"", resourceName.c_str()); + Resource* resource = it->second; + resource->Reload(); + + // Notify parent + auto it2 = m_ResourceParents.find(resource); + if (it2 != m_ResourceParents.end()) { + it2->second->OnChildReloaded(resource); + } + } +} + +unsigned int ResourceManager::GetNewResourceID(unsigned int typeID) +{ + return m_ResourceCount[typeID]++; +} + +bool ResourceManager::IsResourceLoaded(std::string resourceType, std::string resourceName) +{ + return m_ResourceCache.find(std::make_pair(resourceType, resourceName)) != m_ResourceCache.end(); +} + +void ResourceManager::fileWatcherCallback(std::string path, FileWatcher::FileEventFlags flags) +{ + if (flags & FileWatcher::FileEventFlags::SizeChanged || flags & FileWatcher::FileEventFlags::TimestampChanged) { + auto it = m_ResourceFromName.find(path); + if (it != m_ResourceFromName.end()) { + Reload(path); + } + } +} + +void ResourceManager::Update() +{ + m_FileWatcher.Check(); +} + +void ResourceManager::Preload(std::string resourceType, std::string resourceName) +{ + if (IsResourceLoaded(resourceType, resourceName)) { + //LOG_WARNING("Attempted to preload resource \"%s\" multiple times!", resourceName.c_str()); + return; + } + + m_Preloading = true; + LOG_INFO("Preloading resource \"%s\"", resourceName.c_str()); + CreateResource(resourceType, resourceName, nullptr); + m_Preloading = false; +} + +Resource* ResourceManager::Load(std::string resourceType, std::string resourceName, Resource* parent /*= nullptr*/) +{ + auto it = m_ResourceCache.find(std::make_pair(resourceType, resourceName)); + if (it != m_ResourceCache.end()) { + return it->second; + } + + if (m_Preloading) { + LOG_INFO("Preloading resource \"%s\"", resourceName.c_str()); + } else { + LOG_WARNING("Hot-loading resource \"%s\"", resourceName.c_str()); + } + + return CreateResource(resourceType, resourceName, parent); +} + +Resource* ResourceManager::CreateResource(std::string resourceType, std::string resourceName, Resource* parent) +{ + auto facIt = m_FactoryFunctions.find(resourceType); + if (facIt == m_FactoryFunctions.end()) { + LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceType.c_str()); + return nullptr; + } + + // Call the factory function + Resource* resource = facIt->second(resourceName); + // Store IDs + resource->TypeID = GetTypeID(resourceType); + resource->ResourceID = GetNewResourceID(resource->TypeID); + // Cache + m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource; + m_ResourceFromName[resourceName] = resource; + if (parent != nullptr) { + m_ResourceParents[resource] = parent; + } + if (!boost::filesystem::is_directory(resourceName)) { + LOG_DEBUG("Adding watch for %s", resourceName.c_str()); + m_FileWatcher.AddWatch(resourceName, fileWatcherCallback); + } + return resource; +}