-#include <algorithm>
-#include <typeinfo>
-#include <msp/debug/demangle.h>
-#include <msp/strings/format.h>
-#include <msp/time/utils.h>
-#include "resourcemanager.h"
-#include "resources.h"
-#include "resourcewatcher.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-resource_load_error::resource_load_error(const string &name, const string &err):
- runtime_error(format("%s: %s", name, err))
-{ }
-
-resource_load_error::resource_load_error(const string &name, const exception &exc):
- runtime_error(format("%s: %s: %s", name, Debug::demangle(typeid(exc).name()), exc.what()))
-{ }
-
-
-ResourceManager::ResourceManager():
- policy(LOAD_ON_DEMAND),
- async_loads(true),
- total_data_size(0),
- size_limit(0),
- frame(0),
- min_retain_frames(30),
- max_retain_frames(0),
- next_unload(0)
-{ }
-
-ResourceManager::~ResourceManager()
-{
- thread.terminate();
-
- while(!resources.empty())
- resources.begin()->second.resource->set_manager(0);
-}
-
-void ResourceManager::set_loading_policy(LoadingPolicy p)
-{
- policy = p;
-}
-
-void ResourceManager::set_async_loads(bool a)
-{
- async_loads = a;
-}
-
-void ResourceManager::set_size_limit(UInt64 s)
-{
- size_limit = s;
-}
-
-void ResourceManager::set_min_retain_frames(unsigned f)
-{
- min_retain_frames = f;
-}
-
-void ResourceManager::set_max_retain_frames(unsigned f)
-{
- max_retain_frames = f;
-}
-
-void ResourceManager::add_resource(Resource &r)
-{
- MutexLock lock(map_mutex);
- insert_unique(resources, &r, ManagedResource(r));
-}
-
-const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
-{
- MutexLock lock(map_mutex);
- return get_item(resources, &r);
-}
-
-ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
-{
- MutexLock lock(map_mutex);
- return get_item(resources, &r);
-}
-
-void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
-{
- set_resource_location(r, ResourceLocation(c, n));
-}
-
-void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
-{
- {
- MutexLock lock(map_mutex);
- ManagedResource &managed = get_item(resources, &r);
- managed.location = l;
- }
-
- if(policy==LOAD_IMMEDIATELY)
- load_resource(r);
-}
-
-const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
-{
- const ManagedResource &managed = get_managed_resource(r);
- return managed.location.collection ? &managed.location : 0;
-}
-
-void ResourceManager::load_resource(Resource &r)
-{
- ManagedResource &managed = get_managed_resource(r);
- if(!managed.location.collection)
- throw runtime_error("no location");
-
- if(managed.state!=ManagedResource::NOT_LOADED)
- return;
-
- if(async_loads)
- {
- managed.state = ManagedResource::LOAD_QUEUED;
- LoadQueue::iterator i;
- for(i=queue.begin(); (i!=queue.end() && (*i)->load_priority>=managed.load_priority); ++i) ;
- queue.insert(i, &managed);
- }
- else
- {
- managed.start_loading();
- while(!managed.loader->process()) ;
- managed.finish_loading(true);
- total_data_size += managed.data_size;
- }
-}
-
-bool ResourceManager::is_resource_loaded(const Resource &r) const
-{
- ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
- return managed ? managed->state==ManagedResource::LOADED : false;
-}
-
-void ResourceManager::resource_used(const Resource &r)
-{
- ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
- if(!managed)
- return;
- if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
- load_resource(*managed->resource);
-
- managed->last_used = frame;
- if(max_retain_frames && !next_unload)
- next_unload = frame+max_retain_frames+1;
-}
-
-void ResourceManager::remove_resource(Resource &r)
-{
- ManagedResource &managed = get_managed_resource(r);
- ManagedResource::State state = managed.state;
- if(state==ManagedResource::LOAD_QUEUED)
- {
- LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
- if(i!=queue.end())
- queue.erase(i);
- }
- else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
- thread.remove_resource(managed);
-
- for(vector<ResourceWatcher *>::const_iterator i=managed.watchers.begin(); i!=managed.watchers.end(); ++i)
- (*i)->resource_removed(r);
-
- MutexLock lock(map_mutex);
- remove_existing(resources, &r);
-}
-
-void ResourceManager::watch_resource(const Resource &r, ResourceWatcher &w)
-{
- get_managed_resource(r).add_watcher(w);
-}
-
-void ResourceManager::unwatch_resource(const Resource &r, ResourceWatcher &w)
-{
- get_managed_resource(r).remove_watcher(w);
-}
-
-void ResourceManager::tick()
-{
- ++frame;
-
- bool do_unload = (frame>=next_unload);
- if(thread.sync())
- {
- total_data_size += thread.get_and_reset_loaded_data_size();
- do_unload = true;
- }
-
- if(thread.needs_work() && !queue.empty())
- dispatch_work();
-
- if(do_unload)
- {
- MutexLock lock(map_mutex);
- if(max_retain_frames && frame>=next_unload)
- {
- unload_by_age();
-
- next_unload = frame;
- for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
- if(i->second.state==ManagedResource::LOADED)
- next_unload = min(next_unload, i->second.last_used);
- next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
- }
-
- if(size_limit)
- unload_by_size();
- }
-}
-
-void ResourceManager::dispatch_work()
-{
- queue.sort(age_order);
-
- if(queue.front()->last_used+10<frame)
- {
- for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
- (*i)->state = ManagedResource::NOT_LOADED;
- queue.clear();
- return;
- }
-
- while(thread.needs_work() && !queue.empty())
- {
- ManagedResource *managed = queue.front();
- queue.pop_front();
- thread.add_resource(*managed);
- }
-}
-
-void ResourceManager::unload_by_age()
-{
- unsigned unload_limit = frame-max_retain_frames;
-
- for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
- if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
- {
- i->second.unload();
- total_data_size -= i->second.data_size;
- }
-}
-
-void ResourceManager::unload_by_size()
-{
- unsigned unload_limit = frame-min_retain_frames;
-
- while(total_data_size>size_limit)
- {
- ManagedResource *best = 0;
- UInt64 best_impact = 0;
- for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
- if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
- {
- UInt64 impact = i->second.data_size*(frame-i->second.last_used);
- if(!best || impact>best_impact)
- {
- best = &i->second;
- best_impact = impact;
- }
- }
-
- if(!best)
- break;
-
- best->unload();
- total_data_size -= best->data_size;
- }
-}
-
-bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
-{
- return mr1->last_used>mr2->last_used;
-}
-
-
-ResourceManager::ResourceLocation::ResourceLocation():
- collection(0)
-{ }
-
-ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
- collection(&c),
- name(n)
-{ }
-
-
-ResourceManager::ManagedResource::ManagedResource(Resource &r):
- resource(&r),
- load_priority(r.get_load_priority()),
- io(0),
- loader(0),
- state(NOT_LOADED),
- last_used(0),
- data_size(0)
-{ }
-
-void ResourceManager::ManagedResource::start_loading()
-{
- io = location.collection->open_raw(location.name);
- if(!io)
- throw resource_load_error(location.name, "open failed");
-
- const Resources *res = dynamic_cast<Resources *>(location.collection);
- loader = resource->load(*io, res);
- if(!loader)
- {
- delete io;
- io = 0;
- throw logic_error("no loader created");
- }
- state = LOADING;
-}
-
-bool ResourceManager::ManagedResource::process(bool sync)
-{
- while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
- if(loader->process())
- state = LOAD_FINISHED;
-
- return state==LOAD_FINISHED;
-}
-
-void ResourceManager::ManagedResource::finish_loading(bool successful)
-{
- delete loader;
- loader = 0;
- delete io;
- io = 0;
-
- if(successful)
- {
- state = LOADED;
- data_size = resource->get_data_size();
-
- for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
- (*i)->resource_loaded(*resource);
- }
- else
- {
- resource->unload();
- state = NOT_LOADED;
- }
-}
-
-void ResourceManager::ManagedResource::finish_loading()
-{
- finish_loading(state==LOAD_FINISHED);
-}
-
-void ResourceManager::ManagedResource::unload()
-{
- resource->unload();
- state = NOT_LOADED;
-
- for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
- (*i)->resource_unloaded(*resource);
-}
-
-void ResourceManager::ManagedResource::add_watcher(ResourceWatcher &w)
-{
- if(find(watchers.begin(), watchers.end(), &w)==watchers.end())
- watchers.push_back(&w);
-}
-
-void ResourceManager::ManagedResource::remove_watcher(ResourceWatcher &w)
-{
- vector<ResourceWatcher *>::iterator end = remove(watchers.begin(), watchers.end(), &w);
- if(end!=watchers.end())
- watchers.erase(end, watchers.end());
-}
-
-
-ResourceManager::LoadingThread::LoadingThread():
- sem(1),
- capacity(2),
- size(0),
- loaded_data_size(0),
- done(false)
-{
- launch();
-}
-
-void ResourceManager::LoadingThread::main()
-{
- bool wait_for_work = false;
- while(!done)
- {
- if(wait_for_work)
- sem.wait();
-
- if(ManagedResource *managed = front(async_queue))
- {
- try
- {
- managed->process(false);
- }
- catch(const exception &e)
- {
- MutexLock lock(queue_mutex);
- error_queue.push_back(resource_load_error(managed->location.name, e));
- managed->state = ManagedResource::LOAD_ERROR;
- }
-
- MutexLock lock(queue_mutex);
- sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
- wait_for_work = async_queue.empty();
- }
- else
- wait_for_work = true;
- }
-}
-
-ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &que)
-{
- MutexLock lock(queue_mutex);
- if(que.empty())
- return 0;
-
- return que.front();
-}
-
-void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
-{
- r.start_loading();
-
- MutexLock lock(queue_mutex);
- if(r.loader->needs_sync())
- sync_queue.push_back(&r);
- else
- {
- bool was_empty = async_queue.empty();
- async_queue.push_back(&r);
- if(was_empty)
- sem.signal();
- }
-
- ++size;
-}
-
-void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
-{
- while(!try_remove_resource(r))
- Time::sleep(Time::msec);
-
- r.finish_loading();
- if(r.state==ManagedResource::LOADED)
- {
- MutexLock lock(data_size_mutex);
- loaded_data_size += r.data_size;
- }
-}
-
-bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
-{
- MutexLock lock(queue_mutex);
-
- LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
- if(i==async_queue.end())
- {
- i = find(sync_queue.begin(), sync_queue.end(), &r);
- if(i!=sync_queue.end())
- {
- sync_queue.erase(i);
- --size;
- }
- }
- else if(i==async_queue.begin())
- return false;
- else
- {
- async_queue.erase(i);
- --size;
- }
-
- return true;
-}
-
-bool ResourceManager::LoadingThread::sync()
-{
- {
- MutexLock lock(queue_mutex);
-
- if(!error_queue.empty())
- {
- resource_load_error err = error_queue.front();
- error_queue.pop_front();
- throw err;
- }
-
- unsigned async_size = async_queue.size();
- if(async_size==0 && size==capacity)
- ++capacity;
- else if(async_size>2 && capacity>2)
- --capacity;
- }
-
- bool any_finished = false;
- while(ManagedResource *managed = front(sync_queue))
- {
- if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
- {
- managed->finish_loading();
- if(managed->state==ManagedResource::LOADED)
- {
- MutexLock lock(data_size_mutex);
- loaded_data_size += managed->data_size;
- }
- any_finished = true;
- --size;
-
- MutexLock lock(queue_mutex);
- sync_queue.pop_front();
- }
- else
- {
- MutexLock lock(queue_mutex);
- bool was_empty = async_queue.empty();
- async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
- if(was_empty)
- sem.signal();
- }
- }
-
- return any_finished;
-}
-
-UInt64 ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
-{
- MutexLock lock(data_size_mutex);
- UInt64 result = loaded_data_size;
- loaded_data_size = 0;
- return result;
-}
-
-void ResourceManager::LoadingThread::terminate()
-{
- done = true;
- sem.signal();
- join();
- async_queue.clear();
- sync_queue.clear();
-}
-
-} // namespace GL
-} // namespace Msp