1 #include <msp/core/algorithm.h>
3 #include <msp/debug/demangle.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/utils.h>
7 #include "resourcemanager.h"
9 #include "resourceobserver.h"
16 resource_load_error::resource_load_error(const string &name, const string &err):
17 runtime_error(format("%s: %s", name, err))
20 resource_load_error::resource_load_error(const string &name, const exception &exc):
21 runtime_error(format("%s: %s: %s", name, Debug::demangle(typeid(exc).name()), exc.what()))
25 ResourceManager::~ResourceManager()
29 while(!resources.empty())
30 resources.begin()->second.resource->set_manager(0);
33 void ResourceManager::set_loading_policy(LoadingPolicy p)
38 void ResourceManager::set_async_loads(bool a)
43 void ResourceManager::set_size_limit(uint64_t s)
48 void ResourceManager::set_min_retain_frames(unsigned f)
50 min_retain_frames = f;
53 void ResourceManager::set_max_retain_frames(unsigned f)
55 max_retain_frames = f;
58 void ResourceManager::add_resource(Resource &r)
60 if(r.get_manager()!=this)
61 throw invalid_operation("ResourceManager::add_resource");
62 MutexLock lock(map_mutex);
63 insert_unique(resources, &r, ManagedResource(r));
66 void ResourceManager::move_resource(Resource &from, Resource &to)
68 if(to.get_manager()!=this || from.get_manager_data()!=to.get_manager_data())
69 throw invalid_operation("ResourceManager::move_resource");
70 ManagedResource *managed = reinterpret_cast<ManagedResource *>(to.get_manager_data());
71 MutexLock lock(map_mutex);
72 insert_unique(resources, &to, *managed);
73 resources.erase(&from);
76 const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
78 MutexLock lock(map_mutex);
79 return get_item(resources, &r);
82 ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
84 MutexLock lock(map_mutex);
85 return get_item(resources, &r);
88 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
90 set_resource_location(r, ResourceLocation(c, n));
93 void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
96 MutexLock lock(map_mutex);
97 ManagedResource &managed = get_item(resources, &r);
101 if(policy==LOAD_IMMEDIATELY)
105 const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
107 const ManagedResource &managed = get_managed_resource(r);
108 return managed.location.collection ? &managed.location : 0;
111 void ResourceManager::load_resource(Resource &r)
113 ManagedResource &managed = get_managed_resource(r);
114 if(!managed.location.collection)
115 throw runtime_error("no location");
117 if(managed.state!=ManagedResource::NOT_LOADED)
122 managed.state = ManagedResource::LOAD_QUEUED;
123 auto i = find_if(queue, [&managed](ManagedResource *q){ return q->load_priority>managed.load_priority; });
124 queue.insert(i, &managed);
128 managed.start_loading();
129 while(!managed.loader->process()) ;
130 managed.finish_loading(true);
131 total_data_size += managed.data_size;
135 bool ResourceManager::is_resource_loaded(const Resource &r) const
137 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
138 return managed ? managed->state==ManagedResource::LOADED : false;
141 void ResourceManager::resource_used(const Resource &r)
143 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
146 if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
147 load_resource(*managed->resource);
149 managed->last_used = frame;
150 if(max_retain_frames && !next_unload)
151 next_unload = frame+max_retain_frames+1;
154 void ResourceManager::remove_resource(Resource &r)
156 ManagedResource &managed = get_managed_resource(r);
157 ManagedResource::State state = managed.state;
158 if(state==ManagedResource::LOAD_QUEUED)
160 auto i = find(queue, &managed);
164 else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
165 thread.remove_resource(managed);
167 for(ResourceObserver *o: managed.observers)
168 o->resource_removed(r);
170 MutexLock lock(map_mutex);
171 remove_existing(resources, &r);
174 void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
176 get_managed_resource(r).add_observer(w);
179 void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
181 get_managed_resource(r).remove_observer(w);
184 void ResourceManager::tick()
188 bool do_unload = (frame>=next_unload);
191 total_data_size += thread.get_and_reset_loaded_data_size();
195 if(thread.needs_work() && !queue.empty())
200 MutexLock lock(map_mutex);
201 if(max_retain_frames && frame>=next_unload)
206 for(const auto &kvp: resources)
207 if(kvp.second.state==ManagedResource::LOADED)
208 next_unload = min(next_unload, kvp.second.last_used);
209 next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
217 void ResourceManager::dispatch_work()
219 sort(queue, age_order);
221 if(queue.front()->last_used+min_retain_frames<frame)
223 for(ManagedResource *r: queue)
224 r->state = ManagedResource::NOT_LOADED;
229 while(thread.needs_work() && !queue.empty())
231 ManagedResource *managed = queue.front();
233 thread.add_resource(*managed);
237 void ResourceManager::unload_by_age()
239 unsigned unload_limit = frame-max_retain_frames;
241 for(auto &kvp: resources)
242 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
245 total_data_size -= kvp.second.data_size;
249 void ResourceManager::unload_by_size()
251 unsigned unload_limit = frame-min_retain_frames;
253 while(total_data_size>size_limit)
255 ManagedResource *best = 0;
256 uint64_t best_impact = 0;
257 for(auto &kvp: resources)
258 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
260 uint64_t impact = kvp.second.data_size*(frame-kvp.second.last_used);
261 if(!best || impact>best_impact)
264 best_impact = impact;
272 total_data_size -= best->data_size;
276 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
278 return mr1->last_used>mr2->last_used;
282 ResourceManager::ResourceLocation::ResourceLocation():
286 ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
292 ResourceManager::ManagedResource::ManagedResource(Resource &r):
294 load_priority(r.get_load_priority()),
302 void ResourceManager::ManagedResource::start_loading()
304 io = location.collection->open_raw(location.name);
306 throw resource_load_error(location.name, "open failed");
308 const Resources *res = dynamic_cast<Resources *>(location.collection);
309 loader = resource->load(*io, res);
314 throw logic_error("no loader created");
319 bool ResourceManager::ManagedResource::process(bool sync)
321 while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
322 if(loader->process())
323 state = LOAD_FINISHED;
325 return state==LOAD_FINISHED;
328 void ResourceManager::ManagedResource::finish_loading(bool successful)
338 data_size = resource->get_data_size();
340 for(ResourceObserver *o: observers)
341 o->resource_loaded(*resource);
350 void ResourceManager::ManagedResource::finish_loading()
352 finish_loading(state==LOAD_FINISHED);
355 void ResourceManager::ManagedResource::unload()
360 for(ResourceObserver *o: observers)
361 o->resource_unloaded(*resource);
364 void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
366 if(find(observers.begin(), observers.end(), &w)==observers.end())
367 observers.push_back(&w);
370 void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
372 auto end = remove(observers.begin(), observers.end(), &w);
373 if(end!=observers.end())
374 observers.erase(end, observers.end());
378 ResourceManager::LoadingThread::LoadingThread():
379 Thread("ResourceManager"),
389 void ResourceManager::LoadingThread::main()
391 bool wait_for_work = false;
397 if(ManagedResource *managed = front(async_queue))
401 managed->process(false);
403 catch(const exception &e)
405 MutexLock lock(queue_mutex);
406 error_queue.emplace_back(managed->location.name, e);
407 managed->state = ManagedResource::LOAD_ERROR;
410 MutexLock lock(queue_mutex);
411 sync_queue.push_back(async_queue.front());
412 async_queue.pop_front();
413 wait_for_work = async_queue.empty();
416 wait_for_work = true;
420 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(deque<ManagedResource *> &que)
422 MutexLock lock(queue_mutex);
429 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
433 MutexLock lock(queue_mutex);
434 if(r.loader->needs_sync())
435 sync_queue.push_back(&r);
438 bool was_empty = async_queue.empty();
439 async_queue.push_back(&r);
447 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
449 while(!try_remove_resource(r))
450 Time::sleep(Time::msec);
453 if(r.state==ManagedResource::LOADED)
455 MutexLock lock(data_size_mutex);
456 loaded_data_size += r.data_size;
460 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
462 MutexLock lock(queue_mutex);
464 auto i = find(async_queue, &r);
465 if(i==async_queue.end())
467 i = find(sync_queue, &r);
468 if(i!=sync_queue.end())
474 else if(i==async_queue.begin())
478 async_queue.erase(i);
485 bool ResourceManager::LoadingThread::sync()
488 MutexLock lock(queue_mutex);
490 if(!error_queue.empty())
492 resource_load_error err = error_queue.front();
493 error_queue.pop_front();
497 unsigned async_size = async_queue.size();
498 if(async_size==0 && size==capacity)
500 else if(async_size>2 && capacity>2)
504 bool any_finished = false;
505 while(ManagedResource *managed = front(sync_queue))
507 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
509 managed->finish_loading();
510 if(managed->state==ManagedResource::LOADED)
512 MutexLock lock(data_size_mutex);
513 loaded_data_size += managed->data_size;
518 MutexLock lock(queue_mutex);
519 sync_queue.pop_front();
523 MutexLock lock(queue_mutex);
524 bool was_empty = async_queue.empty();
525 async_queue.push_back(sync_queue.front());
526 sync_queue.pop_front();
535 uint64_t ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
537 MutexLock lock(data_size_mutex);
538 uint64_t result = loaded_data_size;
539 loaded_data_size = 0;
543 void ResourceManager::LoadingThread::terminate()