]> git.tdb.fi Git - libs/gl.git/commitdiff
Foundation for a resource management system
authorMikko Rasa <tdb@tdb.fi>
Sun, 17 Aug 2014 19:07:50 +0000 (22:07 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 17 Aug 2014 19:07:50 +0000 (22:07 +0300)
source/resource.cpp [new file with mode: 0644]
source/resource.h [new file with mode: 0644]
source/resourcemanager.cpp [new file with mode: 0644]
source/resourcemanager.h [new file with mode: 0644]
source/resources.cpp
source/resources.h

diff --git a/source/resource.cpp b/source/resource.cpp
new file mode 100644 (file)
index 0000000..c2cf94d
--- /dev/null
@@ -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 (file)
index 0000000..c41b884
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef MSP_GL_RESOURCE_H_
+#define MSP_GL_RESOURCE_H_
+
+#include <msp/io/seekable.h>
+
+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 (file)
index 0000000..81aea8c
--- /dev/null
@@ -0,0 +1,164 @@
+#include <msp/time/utils.h>
+#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 (file)
index 0000000..fb43533
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef MSP_GL_RESOURCEMANAGER_H_
+#define MSP_GL_RESOURCEMANAGER_H_
+
+#include <msp/core/mutex.h>
+#include <msp/core/semaphore.h>
+#include <msp/core/thread.h>
+#include <msp/datafile/collection.h>
+#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<ManagedResource *> LoadQueue;
+
+       LoadingPolicy policy;
+       bool async_loads;
+       std::map<const Resource *, ManagedResource> resources;
+       std::list<ManagedResource *> 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
index 9583994710f5acab1d6b8517e9277ae223b082be..d1fd371c483e71290f71b33139c2b3507af2566f 100644 (file)
@@ -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<Animation>().suffix(".anim").keyword("animation");
        add_type<Armature>().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);
index 51adec4a63c87d7e306a5b694542f77604e5b89b..e2503298c5ce59fe0ba04570700970abeb9d881a 100644 (file)
@@ -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 &);
 };