From: Mikko Rasa Date: Sun, 17 Aug 2014 19:07:50 +0000 (+0300) Subject: Foundation for a resource management system X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=7db48f2836ec42c6e2dbeff336634a03bc6d7a62 Foundation for a resource management system --- diff --git a/source/resource.cpp b/source/resource.cpp new file mode 100644 index 00000000..c2cf94d5 --- /dev/null +++ b/source/resource.cpp @@ -0,0 +1,27 @@ +#include "resource.h" +#include "resourcemanager.h" + +namespace Msp { +namespace GL { + +Resource::Resource(): + manager(0) +{ } + +Resource::~Resource() +{ + if(manager) + manager->remove_resource(*this); +} + +void Resource::set_manager(ResourceManager *m) +{ + if(manager) + manager->remove_resource(*this); + manager = m; + if(manager) + manager->add_resource(*this); +} + +} // namespace GL +} // namespace Msp diff --git a/source/resource.h b/source/resource.h new file mode 100644 index 00000000..c41b8849 --- /dev/null +++ b/source/resource.h @@ -0,0 +1,40 @@ +#ifndef MSP_GL_RESOURCE_H_ +#define MSP_GL_RESOURCE_H_ + +#include + +namespace Msp { +namespace GL { + +class ResourceManager; + +class Resource +{ +public: + class AsyncLoader + { + protected: + AsyncLoader() { } + public: + virtual ~AsyncLoader() { } + + virtual bool needs_sync() const = 0; + virtual bool process() = 0; + }; + +protected: + ResourceManager *manager; + + Resource(); +public: + virtual ~Resource(); + + void set_manager(ResourceManager *); + virtual AsyncLoader *load(IO::Seekable &) = 0; + virtual void unload() = 0; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/resourcemanager.cpp b/source/resourcemanager.cpp new file mode 100644 index 00000000..81aea8c1 --- /dev/null +++ b/source/resourcemanager.cpp @@ -0,0 +1,164 @@ +#include +#include "resourcemanager.h" + +using namespace std; + +namespace Msp { +namespace GL { + +ResourceManager::ResourceManager(): + policy(LOAD_ON_DEMAND), + async_loads(true), + thread(*this) +{ } + +ResourceManager::~ResourceManager() +{ + thread.terminate(); +} + +void ResourceManager::add_resource(Resource &r) +{ + insert_unique(resources, &r, ManagedResource(r)); +} + +void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n) +{ + ManagedResource &managed = get_item(resources, &r); + managed.collection = &c; + managed.name = n; + + if(policy==LOAD_IMMEDIATELY) + load_resource(r); +} + +void ResourceManager::load_resource(const Resource &r) +{ + ManagedResource &managed = get_item(resources, &r); + if(!managed.collection) + throw runtime_error("no location"); + + if(managed.loader) + return; + + managed.io = managed.collection->open_raw(managed.name); + managed.loader = managed.resource->load(*managed.io); + + queue.push_back(&managed); +} + +void ResourceManager::remove_resource(Resource &r) +{ + ManagedResource *loading = thread.get_resource(); + if(loading && loading->resource==&r) + thread.set_resource(0); + + remove_existing(resources, &r); +} + +void ResourceManager::tick() +{ + LoadingThread::State thread_state = thread.get_state(); + if(thread_state==LoadingThread::SYNC_PENDING) + thread.sync(); + else if(thread_state==LoadingThread::IDLE && !queue.empty()) + { + ManagedResource *managed = queue.front(); + queue.pop_front(); + thread.set_resource(managed); + } +} + + +ResourceManager::ManagedResource::ManagedResource(Resource &r): + resource(&r), + collection(0), + io(0), + loader(0) +{ } + + +ResourceManager::LoadingThread::LoadingThread(ResourceManager &m): + manager(m), + sem(1), + resource(0), + state(IDLE) +{ + launch(); +} + +void ResourceManager::LoadingThread::main() +{ + while(state!=TERMINATING) + { + sem.wait(); + + if(state==BUSY) + { + Resource::AsyncLoader *ldr = resource->loader; + bool finished = false; + while(!finished && !ldr->needs_sync()) + finished = ldr->process(); + + if(finished) + state = LOAD_FINISHED; + else + state = SYNC_PENDING; + } + } +} + +void ResourceManager::LoadingThread::set_resource(ManagedResource *r) +{ + if(state!=IDLE) + { + while(state==BUSY) ; + // Force finish to clean up the loader + state = LOAD_FINISHED; + sync(); + } + + resource = r; + state = BUSY; + sem.signal(); +} + +void ResourceManager::LoadingThread::sync() +{ + State s = state; + bool finished = (s==LOAD_FINISHED); + if(s==SYNC_PENDING) + { + Resource::AsyncLoader *ldr = resource->loader; + while(!finished && ldr->needs_sync()) + finished = ldr->process(); + + if(!finished) + { + state = BUSY; + sem.signal(); + return; + } + } + + if(finished) + { + delete resource->loader; + resource->loader = 0; + delete resource->io; + resource->io = 0; + resource = 0; + state = IDLE; + } +} + +void ResourceManager::LoadingThread::terminate() +{ + while(state==BUSY) ; + state = TERMINATING; + sem.signal(); + join(); +} + +} // namespace GL +} // namespace Msp diff --git a/source/resourcemanager.h b/source/resourcemanager.h new file mode 100644 index 00000000..fb435335 --- /dev/null +++ b/source/resourcemanager.h @@ -0,0 +1,94 @@ +#ifndef MSP_GL_RESOURCEMANAGER_H_ +#define MSP_GL_RESOURCEMANAGER_H_ + +#include +#include +#include +#include +#include "resource.h" + +namespace Msp { +namespace GL { + +class ResourceManager +{ +public: + enum LoadingPolicy + { + LOAD_IMMEDIATELY, + LOAD_ON_DEMAND, + LOAD_MANUALLY + }; + +private: + struct ManagedResource + { + Resource *resource; + DataFile::Collection *collection; + std::string name; + IO::Seekable *io; + Resource::AsyncLoader *loader; + + ManagedResource(Resource &); + }; + + class LoadingThread: public Thread + { + public: + enum State + { + IDLE, + SYNC_PENDING, + BUSY, + LOAD_FINISHED, + TERMINATING + }; + + private: + ResourceManager &manager; + Semaphore sem; + ManagedResource *volatile resource; + volatile State state; + + public: + LoadingThread(ResourceManager &); + + private: + virtual void main(); + + public: + void set_resource(ManagedResource *); + ManagedResource *get_resource() const { return resource; } + void sync(); + State get_state() const { return state; } + + void terminate(); + }; + + typedef std::list LoadQueue; + + LoadingPolicy policy; + bool async_loads; + std::map resources; + std::list queue; + LoadingThread thread; + +public: + ResourceManager(); + ~ResourceManager(); + + void set_loading_policy(LoadingPolicy); + void set_async_loads(bool); + + void add_resource(Resource &); + void set_resource_location(Resource &, DataFile::Collection &, const std::string &); + void load_resource(const Resource &); + void remove_resource(Resource &); + + void tick(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/resources.cpp b/source/resources.cpp index 95839947..d1fd371c 100644 --- a/source/resources.cpp +++ b/source/resources.cpp @@ -8,6 +8,7 @@ #include "object.h" #include "pose.h" #include "program.h" +#include "resourcemanager.h" #include "resources.h" #include "technique.h" #include "texture2d.h" @@ -20,7 +21,8 @@ namespace GL { Resources::Resources(): default_tex_filter(LINEAR_MIPMAP_LINEAR), - srgb_conversion(false) + srgb_conversion(false), + resource_manager(0) { add_type().suffix(".anim").keyword("animation"); add_type().suffix(".arma").keyword("armature"); @@ -46,6 +48,11 @@ void Resources::set_srgb_conversion(bool c) srgb_conversion = c; } +void Resources::set_resource_manager(ResourceManager *m) +{ + resource_manager = m; +} + Texture2D *Resources::create_texture2d(const string &name) { string ext = FS::extpart(name); diff --git a/source/resources.h b/source/resources.h index 51adec4a..e2503298 100644 --- a/source/resources.h +++ b/source/resources.h @@ -7,6 +7,7 @@ namespace Msp { namespace GL { +class ResourceManager; class Texture2D; /** @@ -18,6 +19,7 @@ class Resources: virtual public DataFile::Collection private: TextureFilter default_tex_filter; bool srgb_conversion; + ResourceManager *resource_manager; public: Resources(); @@ -30,6 +32,8 @@ public: bool get_srgb_conversion() const { return srgb_conversion; } + void set_resource_manager(ResourceManager *); + protected: Texture2D *create_texture2d(const std::string &); };