diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3ac3038d5..c9460aeca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -68,6 +68,7 @@ set(Magnum_HEADERS Profiler.h Query.h Renderbuffer.h + Resource.h ResourceManager.h Shader.h SizeTraits.h diff --git a/src/Resource.h b/src/Resource.h new file mode 100644 index 000000000..0909d5eb5 --- /dev/null +++ b/src/Resource.h @@ -0,0 +1,264 @@ +#ifndef Magnum_Resource_h +#define Magnum_Resource_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file /Resource.h + * @brief Class Magnum::ResourceKey, Magnum::Resource, enum Magnum::ResourceState, Magnum::ResourceDataState, Magnum::ResourcePolicy + */ + +namespace Magnum { + +/** @relates ResourceManager + * @brief %Resource state + * + * @see Resource::state(), ResourceManager::state() + */ +enum class ResourceState: std::uint8_t { + /** The resource is not yet loaded. */ + NotLoaded, + + /** The resource is not yet loaded and fallback resource is used instead. */ + Fallback, + + /** The resource is loaded, but can be changed by the manager at any time. */ + Mutable, + + /** The resource is loaded and won't be changed by the manager anymore. */ + Final +}; + +/** @relates ResourceManager + * @brief %Resource data state + * + * @see ResourceManager::set() + */ +enum class ResourceDataState: std::uint8_t { + /** + * The resource can be changed by the manager in the future. This is + * slower, as Resource needs to ask the manager for new version every time + * the data are accessed, but allows changing the data for e.g. debugging + * purposes. + */ + Mutable = int(ResourceState::Mutable), + + /** + * The resource cannot be changed by the manager in the future. This is + * faster, as Resource instances will ask for the data only one time, thus + * suitable for production code. + */ + Final = int(ResourceState::Final) +}; + +/** @relates ResourceManager +@brief %Resource policy + +@see ResourceManager::set(), ResourceManager::free() + */ +enum class ResourcePolicy: std::uint8_t { + /** The resource will stay resident for whole lifetime of resource manager. */ + Resident, + + /** + * The resource will be unloaded when manually calling + * ResourceManager::free() if nothing references it. + */ + Manual, + + /** The resource will be unloaded when last reference to it is gone. */ + ReferenceCounted +}; + +/** +@brief Key for accessing resource + +@see ResourceManager::referenceCount(), ResourceManager::state(), + ResourceManager::get(), ResourceManager::set(), Resource::key() +*/ +class ResourceKey: public Corrade::Utility::MurmurHash2::Digest { + public: + /** + * @brief Default constructor + * + * Creates zero key. Note that it is not the same as calling other + * constructors with empty string. + */ + inline constexpr ResourceKey() {} + + /** @brief Constructor */ + inline ResourceKey(const std::string& key): Corrade::Utility::MurmurHash2::Digest(Corrade::Utility::MurmurHash2()(key)) {} + + /** + * @brief Constructor + * @todo constexpr + */ + template inline constexpr ResourceKey(const char(&key)[size]): Corrade::Utility::MurmurHash2::Digest(Corrade::Utility::MurmurHash2()(key)) {} +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + template class ResourceManagerData; +} +#endif + +/** +@brief %Resource reference + +See ResourceManager for more information. +*/ +template class Resource { + friend class Implementation::ResourceManagerData; + + public: + /** + * @brief Default constructor + * + * Creates empty resource. Resources are acquired from the manager by + * calling ResourceManager::get(). + */ + inline Resource(): manager(nullptr), lastCheck(0), _state(ResourceState::Final), data(nullptr) {} + + /** @brief Copy constructor */ + inline Resource(const Resource& other): manager(other.manager), _key(other._key), lastCheck(other.lastCheck), _state(other._state), data(other.data) { + if(manager) manager->incrementReferenceCount(_key); + } + + /** @brief Move constructor */ + inline Resource(Resource&& other): manager(other.manager), _key(other._key), lastCheck(other.lastCheck), _state(other._state), data(other.data) { + other.manager = nullptr; + } + + /** @brief Destructor */ + inline ~Resource() { + if(manager) manager->decrementReferenceCount(_key); + } + + /** @brief Assignment operator */ + Resource& operator=(const Resource& other) { + if(manager) manager->decrementReferenceCount(_key); + + manager = other.manager; + _key = other._key; + lastCheck = other.lastCheck; + _state = other._state; + data = other.data; + + if(manager) manager->incrementReferenceCount(_key); + return *this; + } + + /** @brief Assignment move operator */ + Resource& operator=(Resource&& other) { + if(manager) manager->decrementReferenceCount(_key); + + manager = other.manager; + _key = other._key; + lastCheck = other.lastCheck; + _state = other._state; + data = other.data; + + other.manager = nullptr; + return *this; + } + + /** @brief Resource key */ + inline ResourceKey key() const { return _key; } + + /** + * @brief %Resource state + * + * @see operator bool() + */ + inline ResourceState state() { + acquire(); + return _state; + } + + /** + * @brief Whether the resource is available + * @return False when resource is not loaded and no fallback is + * available, true otherwise. + * + * @see state() + */ + inline operator bool() { + acquire(); + return data; + } + + /** + * @brief %Resource data + * + * The resource must be loaded before accessing it. Use boolean + * conversion operator or state() for testing whether it is loaded. + */ + inline operator U*() { + acquire(); + CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), nullptr); + return static_cast(data); + } + + /** @overload */ + inline U* operator->() { + acquire(); + CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), nullptr); + return static_cast(data); + } + + /** @overload */ + inline U& operator*() { + acquire(); + CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), *static_cast(data)); + return *static_cast(data); + } + + private: + inline Resource(Implementation::ResourceManagerData* manager, ResourceKey key): manager(manager), _key(key), lastCheck(0), _state(ResourceState::NotLoaded), data(nullptr) { + manager->incrementReferenceCount(key); + } + + void acquire() { + /* The data are already final, nothing to do */ + if(_state == ResourceState::Final) return; + + /* Nothing changed since last check */ + if(manager->lastChange() < lastCheck) return; + + /* Acquire new data and save last check time */ + const typename Implementation::ResourceManagerData::Data& d = manager->data(_key); + lastCheck = manager->lastChange(); + + /* Try to get the data */ + if((data = d.data)) + _state = static_cast(d.state); + else if((data = manager->fallback())) + _state = ResourceState::Fallback; + else + _state = ResourceState::NotLoaded; + } + + Implementation::ResourceManagerData* manager; + ResourceKey _key; + std::size_t lastCheck; + ResourceState _state; + T* data; +}; + +} + +/* Make the definition complete */ +#include "ResourceManager.h" + +#endif diff --git a/src/ResourceManager.h b/src/ResourceManager.h index 22a7097e4..696eb3640 100644 --- a/src/ResourceManager.h +++ b/src/ResourceManager.h @@ -16,101 +16,15 @@ */ /** @file - * @brief Class Magnum::ResourceManager, Magnum::ResourceKey, Magnum::Resource, enum Magnum::ResourceState, Magnum::ResourceDataState, Magnum::ResourcePolicy + * @brief Class Magnum::ResourceManager */ #include #include -namespace Magnum { - -/** @relates ResourceManager - * @brief %Resource state - * - * @see Resource::state(), ResourceManager::state() - */ -enum class ResourceState: std::uint8_t { - /** The resource is not yet loaded. */ - NotLoaded, - - /** The resource is not yet loaded and fallback resource is used instead. */ - Fallback, - - /** The resource is loaded, but can be changed by the manager at any time. */ - Mutable, - - /** The resource is loaded and won't be changed by the manager anymore. */ - Final -}; - -/** @relates ResourceManager - * @brief %Resource data state - * - * @see ResourceManager::set() - */ -enum class ResourceDataState: std::uint8_t { - /** - * The resource can be changed by the manager in the future. This is - * slower, as Resource needs to ask the manager for new version every time - * the data are accessed, but allows changing the data for e.g. debugging - * purposes. - */ - Mutable = int(ResourceState::Mutable), - - /** - * The resource cannot be changed by the manager in the future. This is - * faster, as Resource instances will ask for the data only one time, thus - * suitable for production code. - */ - Final = int(ResourceState::Final) -}; - -/** @relates ResourceManager -@brief %Resource policy - -@see ResourceManager::set(), ResourceManager::free() - */ -enum class ResourcePolicy: std::uint8_t { - /** The resource will stay resident for whole lifetime of resource manager. */ - Resident, - - /** - * The resource will be unloaded when manually calling - * ResourceManager::free() if nothing references it. - */ - Manual, - - /** The resource will be unloaded when last reference to it is gone. */ - ReferenceCounted -}; - -/** -@brief Key for accessing resource - -@see ResourceManager::referenceCount(), ResourceManager::state(), - ResourceManager::get(), ResourceManager::set(), Resource::key() -*/ -class ResourceKey: public Corrade::Utility::MurmurHash2::Digest { - public: - /** - * @brief Default constructor - * - * Creates zero key. Note that it is not the same as calling other - * constructors with empty string. - */ - inline constexpr ResourceKey() {} - - /** @brief Constructor */ - inline ResourceKey(const std::string& key): Corrade::Utility::MurmurHash2::Digest(Corrade::Utility::MurmurHash2()(key)) {} +#include "Resource.h" - /** - * @brief Constructor - * @todo constexpr - */ - template inline constexpr ResourceKey(const char(&key)[size]): Corrade::Utility::MurmurHash2::Digest(Corrade::Utility::MurmurHash2()(key)) {} -}; - -template class Resource; +namespace Magnum { #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { @@ -249,149 +163,6 @@ namespace Implementation { } #endif -/** -@brief %Resource reference - -See ResourceManager for more information. -*/ -template class Resource { - friend class Implementation::ResourceManagerData; - - public: - /** - * @brief Default constructor - * - * Creates empty resource. Resources are acquired from the manager by - * calling ResourceManager::get(). - */ - inline Resource(): manager(nullptr), lastCheck(0), _state(ResourceState::Final), data(nullptr) {} - - /** @brief Copy constructor */ - inline Resource(const Resource& other): manager(other.manager), _key(other._key), lastCheck(other.lastCheck), _state(other._state), data(other.data) { - if(manager) manager->incrementReferenceCount(_key); - } - - /** @brief Move constructor */ - inline Resource(Resource&& other): manager(other.manager), _key(other._key), lastCheck(other.lastCheck), _state(other._state), data(other.data) { - other.manager = nullptr; - } - - /** @brief Destructor */ - inline ~Resource() { - if(manager) manager->decrementReferenceCount(_key); - } - - /** @brief Assignment operator */ - Resource& operator=(const Resource& other) { - if(manager) manager->decrementReferenceCount(_key); - - manager = other.manager; - _key = other._key; - lastCheck = other.lastCheck; - _state = other._state; - data = other.data; - - if(manager) manager->incrementReferenceCount(_key); - return *this; - } - - /** @brief Assignment move operator */ - Resource& operator=(Resource&& other) { - if(manager) manager->decrementReferenceCount(_key); - - manager = other.manager; - _key = other._key; - lastCheck = other.lastCheck; - _state = other._state; - data = other.data; - - other.manager = nullptr; - return *this; - } - - /** @brief Resource key */ - inline ResourceKey key() const { return _key; } - - /** - * @brief %Resource state - * - * @see operator bool() - */ - inline ResourceState state() { - acquire(); - return _state; - } - - /** - * @brief Whether the resource is available - * @return False when resource is not loaded and no fallback is - * available, true otherwise. - * - * @see state() - */ - inline operator bool() { - acquire(); - return data; - } - - /** - * @brief %Resource data - * - * The resource must be loaded before accessing it. Use boolean - * conversion operator or state() for testing whether it is loaded. - */ - inline operator U*() { - acquire(); - CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), nullptr); - return static_cast(data); - } - - /** @overload */ - inline U* operator->() { - acquire(); - CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), nullptr); - return static_cast(data); - } - - /** @overload */ - inline U& operator*() { - acquire(); - CORRADE_ASSERT(data, "Resource: accessing not loaded data with key" << key(), *static_cast(data)); - return *static_cast(data); - } - - private: - inline Resource(Implementation::ResourceManagerData* manager, ResourceKey key): manager(manager), _key(key), lastCheck(0), _state(ResourceState::NotLoaded), data(nullptr) { - manager->incrementReferenceCount(key); - } - - void acquire() { - /* The data are already final, nothing to do */ - if(_state == ResourceState::Final) return; - - /* Nothing changed since last check */ - if(manager->lastChange() < lastCheck) return; - - /* Acquire new data and save last check time */ - const typename Implementation::ResourceManagerData::Data& d = manager->data(_key); - lastCheck = manager->lastChange(); - - /* Try to get the data */ - if((data = d.data)) - _state = static_cast(d.state); - else if((data = manager->fallback())) - _state = ResourceState::Fallback; - else - _state = ResourceState::NotLoaded; - } - - Implementation::ResourceManagerData* manager; - ResourceKey _key; - std::size_t lastCheck; - ResourceState _state; - T* data; -}; - /** @brief %Resource manager