3 #include <msp/debug/demangle.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/utils.h>
6 #include "resourcemanager.h"
7 #include "resourcewatcher.h"
14 resource_load_error::resource_load_error(const string &name, const exception &exc):
15 runtime_error(format("%s: %s: %s", name, Debug::demangle(typeid(exc).name()), exc.what()))
19 ResourceManager::ResourceManager():
20 policy(LOAD_ON_DEMAND),
24 min_retain_frames(30),
29 ResourceManager::~ResourceManager()
33 while(!resources.empty())
34 resources.begin()->second.resource->set_manager(0);
37 void ResourceManager::set_loading_policy(LoadingPolicy p)
42 void ResourceManager::set_async_loads(bool a)
47 void ResourceManager::set_size_limit(UInt64 s)
52 void ResourceManager::set_min_retain_frames(unsigned f)
54 min_retain_frames = f;
57 void ResourceManager::set_max_retain_frames(unsigned f)
59 max_retain_frames = f;
62 void ResourceManager::add_resource(Resource &r)
64 insert_unique(resources, &r, ManagedResource(r));
67 void *ResourceManager::get_data_for_resource(const Resource &r)
69 return &get_item(resources, &r);
72 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
74 ManagedResource &managed = get_item(resources, &r);
75 managed.collection = &c;
78 if(policy==LOAD_IMMEDIATELY)
82 void ResourceManager::load_resource(Resource &r)
84 ManagedResource &managed = get_item(resources, &r);
85 if(!managed.collection)
86 throw runtime_error("no location");
88 if(managed.state!=ManagedResource::NOT_LOADED)
93 managed.state = ManagedResource::LOAD_QUEUED;
94 queue.push_back(&managed);
98 managed.start_loading();
99 while(!managed.loader->process()) ;
100 managed.finish_loading(true);
104 bool ResourceManager::is_resource_loaded(const Resource &r)
106 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
107 return managed ? managed->state==ManagedResource::LOADED : false;
110 void ResourceManager::resource_used(const Resource &r)
112 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
113 if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
114 load_resource(*managed->resource);
116 managed->last_used = frame;
117 if(max_retain_frames && !next_unload)
118 next_unload = frame+max_retain_frames+1;
121 void ResourceManager::remove_resource(Resource &r)
123 ManagedResource &managed = get_item(resources, &r);
124 ManagedResource::State state = managed.state;
125 if(state==ManagedResource::LOAD_QUEUED)
127 LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
131 else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
132 thread.remove_resource(managed);
133 remove_existing(resources, &r);
136 void ResourceManager::watch_resource(const Resource &r, ResourceWatcher &w)
138 get_item(resources, &r).add_watcher(w);
141 void ResourceManager::unwatch_resource(const Resource &r, ResourceWatcher &w)
143 get_item(resources, &r).remove_watcher(w);
146 void ResourceManager::tick()
150 bool do_unload = (frame>=next_unload);
154 if(thread.needs_work() && !queue.empty())
159 if(max_retain_frames && frame>=next_unload)
164 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
165 if(i->second.state==ManagedResource::LOADED)
166 next_unload = min(next_unload, i->second.last_used);
167 next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
175 void ResourceManager::dispatch_work()
177 queue.sort(age_order);
179 if(queue.front()->last_used+10<frame)
181 for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
182 (*i)->state = ManagedResource::NOT_LOADED;
187 while(thread.needs_work() && !queue.empty())
189 ManagedResource *managed = queue.front();
191 thread.add_resource(*managed);
195 void ResourceManager::unload_by_age()
197 unsigned unload_limit = frame-max_retain_frames;
199 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
200 if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
204 void ResourceManager::unload_by_size()
206 unsigned unload_limit = frame-min_retain_frames;
208 while(get_total_data_size()>size_limit)
210 ManagedResource *best = 0;
211 UInt64 best_impact = 0;
212 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
213 if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
215 UInt64 impact = i->second.data_size*(frame-i->second.last_used);
216 if(!best || impact>best_impact)
219 best_impact = impact;
230 UInt64 ResourceManager::get_total_data_size() const
233 for(ResourceMap::const_iterator i=resources.begin(); i!=resources.end(); ++i)
234 if(i->second.state==ManagedResource::LOADED)
235 total += i->second.data_size;
239 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
241 return mr1->last_used>mr2->last_used;
245 ResourceManager::ManagedResource::ManagedResource(Resource &r):
255 void ResourceManager::ManagedResource::start_loading()
257 io = collection->open_raw(name);
258 loader = resource->load(*io);
263 throw logic_error("no loader created");
268 bool ResourceManager::ManagedResource::process(bool sync)
270 while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
271 if(loader->process())
272 state = LOAD_FINISHED;
274 return state==LOAD_FINISHED;
277 void ResourceManager::ManagedResource::finish_loading(bool successful)
287 data_size = resource->get_data_size();
289 for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
290 (*i)->resource_loaded(*resource);
299 void ResourceManager::ManagedResource::finish_loading()
301 finish_loading(state==LOAD_FINISHED);
304 void ResourceManager::ManagedResource::unload()
309 for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
310 (*i)->resource_unloaded(*resource);
313 void ResourceManager::ManagedResource::add_watcher(ResourceWatcher &w)
315 if(find(watchers.begin(), watchers.end(), &w)==watchers.end())
316 watchers.push_back(&w);
319 void ResourceManager::ManagedResource::remove_watcher(ResourceWatcher &w)
321 vector<ResourceWatcher *>::iterator end = remove(watchers.begin(), watchers.end(), &w);
322 if(end!=watchers.end())
323 watchers.erase(end, watchers.end());
327 ResourceManager::LoadingThread::LoadingThread():
336 void ResourceManager::LoadingThread::main()
338 bool wait_for_work = false;
344 if(ManagedResource *managed = front(async_queue))
348 managed->process(false);
350 catch(const exception &e)
352 MutexLock lock(queue_mutex);
353 error_queue.push_back(resource_load_error(managed->name, e));
354 managed->state = ManagedResource::LOAD_ERROR;
357 MutexLock lock(queue_mutex);
358 sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
359 wait_for_work = async_queue.empty();
362 wait_for_work = true;
366 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &queue)
368 MutexLock lock(queue_mutex);
372 return queue.front();
375 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
379 MutexLock lock(queue_mutex);
380 if(r.loader->needs_sync())
381 sync_queue.push_back(&r);
384 bool was_empty = async_queue.empty();
385 async_queue.push_back(&r);
393 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
395 while(!try_remove_resource(r))
396 Time::sleep(Time::msec);
401 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
403 MutexLock lock(queue_mutex);
405 LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
406 if(i==async_queue.end())
408 i = find(sync_queue.begin(), sync_queue.end(), &r);
409 if(i!=sync_queue.end())
412 else if(i==async_queue.begin())
415 async_queue.erase(i);
420 bool ResourceManager::LoadingThread::sync()
423 MutexLock lock(queue_mutex);
425 if(!error_queue.empty())
427 resource_load_error err = error_queue.front();
428 error_queue.pop_front();
432 unsigned async_size = async_queue.size();
433 if(async_size==0 && size==capacity)
435 else if(async_size>2 && capacity>2)
439 bool any_finished = false;
440 while(ManagedResource *managed = front(sync_queue))
442 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
444 managed->finish_loading();
448 MutexLock lock(queue_mutex);
449 sync_queue.pop_front();
453 MutexLock lock(queue_mutex);
454 bool was_empty = async_queue.empty();
455 async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
464 void ResourceManager::LoadingThread::terminate()