3 #include <msp/debug/demangle.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/utils.h>
6 #include "resourcemanager.h"
8 #include "resourcewatcher.h"
15 resource_load_error::resource_load_error(const string &name, const string &err):
16 runtime_error(format("%s: %s", name, err))
19 resource_load_error::resource_load_error(const string &name, const exception &exc):
20 runtime_error(format("%s: %s: %s", name, Debug::demangle(typeid(exc).name()), exc.what()))
24 ResourceManager::ResourceManager():
25 policy(LOAD_ON_DEMAND),
29 min_retain_frames(30),
34 ResourceManager::~ResourceManager()
38 while(!resources.empty())
39 resources.begin()->second.resource->set_manager(0);
42 void ResourceManager::set_loading_policy(LoadingPolicy p)
47 void ResourceManager::set_async_loads(bool a)
52 void ResourceManager::set_size_limit(UInt64 s)
57 void ResourceManager::set_min_retain_frames(unsigned f)
59 min_retain_frames = f;
62 void ResourceManager::set_max_retain_frames(unsigned f)
64 max_retain_frames = f;
67 void ResourceManager::add_resource(Resource &r)
69 MutexLock lock(map_mutex);
70 insert_unique(resources, &r, ManagedResource(r));
73 const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
75 MutexLock lock(map_mutex);
76 return get_item(resources, &r);
79 ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
81 MutexLock lock(map_mutex);
82 return get_item(resources, &r);
85 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
87 set_resource_location(r, ResourceLocation(c, n));
90 void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
93 MutexLock lock(map_mutex);
94 ManagedResource &managed = get_item(resources, &r);
98 if(policy==LOAD_IMMEDIATELY)
102 const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
104 const ManagedResource &managed = get_managed_resource(r);
105 return managed.location.collection ? &managed.location : 0;
108 void ResourceManager::load_resource(Resource &r)
110 ManagedResource &managed = get_managed_resource(r);
111 if(!managed.location.collection)
112 throw runtime_error("no location");
114 if(managed.state!=ManagedResource::NOT_LOADED)
119 managed.state = ManagedResource::LOAD_QUEUED;
120 LoadQueue::iterator i;
121 for(i=queue.begin(); (i!=queue.end() && (*i)->load_priority>=managed.load_priority); ++i) ;
122 queue.insert(i, &managed);
126 managed.start_loading();
127 while(!managed.loader->process()) ;
128 managed.finish_loading(true);
129 total_data_size += managed.data_size;
133 bool ResourceManager::is_resource_loaded(const Resource &r) const
135 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
136 return managed ? managed->state==ManagedResource::LOADED : false;
139 void ResourceManager::resource_used(const Resource &r)
141 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
144 if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
145 load_resource(*managed->resource);
147 managed->last_used = frame;
148 if(max_retain_frames && !next_unload)
149 next_unload = frame+max_retain_frames+1;
152 void ResourceManager::remove_resource(Resource &r)
154 ManagedResource &managed = get_managed_resource(r);
155 ManagedResource::State state = managed.state;
156 if(state==ManagedResource::LOAD_QUEUED)
158 LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
162 else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
163 thread.remove_resource(managed);
165 MutexLock lock(map_mutex);
166 remove_existing(resources, &r);
169 void ResourceManager::watch_resource(const Resource &r, ResourceWatcher &w)
171 get_managed_resource(r).add_watcher(w);
174 void ResourceManager::unwatch_resource(const Resource &r, ResourceWatcher &w)
176 get_managed_resource(r).remove_watcher(w);
179 void ResourceManager::tick()
183 bool do_unload = (frame>=next_unload);
186 total_data_size += thread.get_and_reset_loaded_data_size();
190 if(thread.needs_work() && !queue.empty())
195 MutexLock lock(map_mutex);
196 if(max_retain_frames && frame>=next_unload)
201 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
202 if(i->second.state==ManagedResource::LOADED)
203 next_unload = min(next_unload, i->second.last_used);
204 next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
212 void ResourceManager::dispatch_work()
214 queue.sort(age_order);
216 if(queue.front()->last_used+10<frame)
218 for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
219 (*i)->state = ManagedResource::NOT_LOADED;
224 while(thread.needs_work() && !queue.empty())
226 ManagedResource *managed = queue.front();
228 thread.add_resource(*managed);
232 void ResourceManager::unload_by_age()
234 unsigned unload_limit = frame-max_retain_frames;
236 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
237 if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
240 total_data_size -= i->second.data_size;
244 void ResourceManager::unload_by_size()
246 unsigned unload_limit = frame-min_retain_frames;
248 while(total_data_size>size_limit)
250 ManagedResource *best = 0;
251 UInt64 best_impact = 0;
252 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
253 if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
255 UInt64 impact = i->second.data_size*(frame-i->second.last_used);
256 if(!best || impact>best_impact)
259 best_impact = impact;
267 total_data_size -= best->data_size;
271 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
273 return mr1->last_used>mr2->last_used;
277 ResourceManager::ResourceLocation::ResourceLocation():
281 ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
287 ResourceManager::ManagedResource::ManagedResource(Resource &r):
289 load_priority(r.get_load_priority()),
297 void ResourceManager::ManagedResource::start_loading()
299 io = location.collection->open_raw(location.name);
301 throw resource_load_error(location.name, "open failed");
303 const Resources *res = dynamic_cast<Resources *>(location.collection);
304 loader = resource->load(*io, res);
309 throw logic_error("no loader created");
314 bool ResourceManager::ManagedResource::process(bool sync)
316 while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
317 if(loader->process())
318 state = LOAD_FINISHED;
320 return state==LOAD_FINISHED;
323 void ResourceManager::ManagedResource::finish_loading(bool successful)
333 data_size = resource->get_data_size();
335 for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
336 (*i)->resource_loaded(*resource);
345 void ResourceManager::ManagedResource::finish_loading()
347 finish_loading(state==LOAD_FINISHED);
350 void ResourceManager::ManagedResource::unload()
355 for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
356 (*i)->resource_unloaded(*resource);
359 void ResourceManager::ManagedResource::add_watcher(ResourceWatcher &w)
361 if(find(watchers.begin(), watchers.end(), &w)==watchers.end())
362 watchers.push_back(&w);
365 void ResourceManager::ManagedResource::remove_watcher(ResourceWatcher &w)
367 vector<ResourceWatcher *>::iterator end = remove(watchers.begin(), watchers.end(), &w);
368 if(end!=watchers.end())
369 watchers.erase(end, watchers.end());
373 ResourceManager::LoadingThread::LoadingThread():
382 void ResourceManager::LoadingThread::main()
384 bool wait_for_work = false;
390 if(ManagedResource *managed = front(async_queue))
394 managed->process(false);
396 catch(const exception &e)
398 MutexLock lock(queue_mutex);
399 error_queue.push_back(resource_load_error(managed->location.name, e));
400 managed->state = ManagedResource::LOAD_ERROR;
403 MutexLock lock(queue_mutex);
404 sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
405 wait_for_work = async_queue.empty();
408 wait_for_work = true;
412 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &queue)
414 MutexLock lock(queue_mutex);
418 return queue.front();
421 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
425 MutexLock lock(queue_mutex);
426 if(r.loader->needs_sync())
427 sync_queue.push_back(&r);
430 bool was_empty = async_queue.empty();
431 async_queue.push_back(&r);
439 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
441 while(!try_remove_resource(r))
442 Time::sleep(Time::msec);
445 if(r.state==ManagedResource::LOADED)
447 MutexLock lock(data_size_mutex);
448 loaded_data_size += r.data_size;
452 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
454 MutexLock lock(queue_mutex);
456 LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
457 if(i==async_queue.end())
459 i = find(sync_queue.begin(), sync_queue.end(), &r);
460 if(i!=sync_queue.end())
466 else if(i==async_queue.begin())
470 async_queue.erase(i);
477 bool ResourceManager::LoadingThread::sync()
480 MutexLock lock(queue_mutex);
482 if(!error_queue.empty())
484 resource_load_error err = error_queue.front();
485 error_queue.pop_front();
489 unsigned async_size = async_queue.size();
490 if(async_size==0 && size==capacity)
492 else if(async_size>2 && capacity>2)
496 bool any_finished = false;
497 while(ManagedResource *managed = front(sync_queue))
499 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
501 managed->finish_loading();
502 if(managed->state==ManagedResource::LOADED)
504 MutexLock lock(data_size_mutex);
505 loaded_data_size += managed->data_size;
510 MutexLock lock(queue_mutex);
511 sync_queue.pop_front();
515 MutexLock lock(queue_mutex);
516 bool was_empty = async_queue.empty();
517 async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
526 UInt64 ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
528 MutexLock lock(data_size_mutex);
529 UInt64 result = loaded_data_size;
530 loaded_data_size = 0;
534 void ResourceManager::LoadingThread::terminate()