1 #include <msp/core/algorithm.h>
3 #include <msp/debug/demangle.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/utils.h>
6 #include "resourcemanager.h"
8 #include "resourceobserver.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),
30 min_retain_frames(30),
35 ResourceManager::~ResourceManager()
39 while(!resources.empty())
40 resources.begin()->second.resource->set_manager(0);
43 void ResourceManager::set_loading_policy(LoadingPolicy p)
48 void ResourceManager::set_async_loads(bool a)
53 void ResourceManager::set_size_limit(uint64_t s)
58 void ResourceManager::set_min_retain_frames(unsigned f)
60 min_retain_frames = f;
63 void ResourceManager::set_max_retain_frames(unsigned f)
65 max_retain_frames = f;
68 void ResourceManager::add_resource(Resource &r)
70 MutexLock lock(map_mutex);
71 insert_unique(resources, &r, ManagedResource(r));
74 const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
76 MutexLock lock(map_mutex);
77 return get_item(resources, &r);
80 ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
82 MutexLock lock(map_mutex);
83 return get_item(resources, &r);
86 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
88 set_resource_location(r, ResourceLocation(c, n));
91 void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
94 MutexLock lock(map_mutex);
95 ManagedResource &managed = get_item(resources, &r);
99 if(policy==LOAD_IMMEDIATELY)
103 const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
105 const ManagedResource &managed = get_managed_resource(r);
106 return managed.location.collection ? &managed.location : 0;
109 void ResourceManager::load_resource(Resource &r)
111 ManagedResource &managed = get_managed_resource(r);
112 if(!managed.location.collection)
113 throw runtime_error("no location");
115 if(managed.state!=ManagedResource::NOT_LOADED)
120 managed.state = ManagedResource::LOAD_QUEUED;
121 auto i = find_if(queue, [&managed](ManagedResource *q){ return q->load_priority>managed.load_priority; });
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 auto i = find(queue, &managed);
162 else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
163 thread.remove_resource(managed);
165 for(ResourceObserver *o: managed.observers)
166 o->resource_removed(r);
168 MutexLock lock(map_mutex);
169 remove_existing(resources, &r);
172 void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
174 get_managed_resource(r).add_observer(w);
177 void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
179 get_managed_resource(r).remove_observer(w);
182 void ResourceManager::tick()
186 bool do_unload = (frame>=next_unload);
189 total_data_size += thread.get_and_reset_loaded_data_size();
193 if(thread.needs_work() && !queue.empty())
198 MutexLock lock(map_mutex);
199 if(max_retain_frames && frame>=next_unload)
204 for(const auto &kvp: resources)
205 if(kvp.second.state==ManagedResource::LOADED)
206 next_unload = min(next_unload, kvp.second.last_used);
207 next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
215 void ResourceManager::dispatch_work()
217 sort(queue, age_order);
219 if(queue.front()->last_used+min_retain_frames<frame)
221 for(ManagedResource *r: queue)
222 r->state = ManagedResource::NOT_LOADED;
227 while(thread.needs_work() && !queue.empty())
229 ManagedResource *managed = queue.front();
231 thread.add_resource(*managed);
235 void ResourceManager::unload_by_age()
237 unsigned unload_limit = frame-max_retain_frames;
239 for(auto &kvp: resources)
240 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
243 total_data_size -= kvp.second.data_size;
247 void ResourceManager::unload_by_size()
249 unsigned unload_limit = frame-min_retain_frames;
251 while(total_data_size>size_limit)
253 ManagedResource *best = 0;
254 uint64_t best_impact = 0;
255 for(auto &kvp: resources)
256 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
258 uint64_t impact = kvp.second.data_size*(frame-kvp.second.last_used);
259 if(!best || impact>best_impact)
262 best_impact = impact;
270 total_data_size -= best->data_size;
274 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
276 return mr1->last_used>mr2->last_used;
280 ResourceManager::ResourceLocation::ResourceLocation():
284 ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
290 ResourceManager::ManagedResource::ManagedResource(Resource &r):
292 load_priority(r.get_load_priority()),
300 void ResourceManager::ManagedResource::start_loading()
302 io = location.collection->open_raw(location.name);
304 throw resource_load_error(location.name, "open failed");
306 const Resources *res = dynamic_cast<Resources *>(location.collection);
307 loader = resource->load(*io, res);
312 throw logic_error("no loader created");
317 bool ResourceManager::ManagedResource::process(bool sync)
319 while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
320 if(loader->process())
321 state = LOAD_FINISHED;
323 return state==LOAD_FINISHED;
326 void ResourceManager::ManagedResource::finish_loading(bool successful)
336 data_size = resource->get_data_size();
338 for(ResourceObserver *o: observers)
339 o->resource_loaded(*resource);
348 void ResourceManager::ManagedResource::finish_loading()
350 finish_loading(state==LOAD_FINISHED);
353 void ResourceManager::ManagedResource::unload()
358 for(ResourceObserver *o: observers)
359 o->resource_unloaded(*resource);
362 void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
364 if(find(observers.begin(), observers.end(), &w)==observers.end())
365 observers.push_back(&w);
368 void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
370 auto end = remove(observers.begin(), observers.end(), &w);
371 if(end!=observers.end())
372 observers.erase(end, observers.end());
376 ResourceManager::LoadingThread::LoadingThread():
377 Thread("ResourceManager"),
387 void ResourceManager::LoadingThread::main()
389 bool wait_for_work = false;
395 if(ManagedResource *managed = front(async_queue))
399 managed->process(false);
401 catch(const exception &e)
403 MutexLock lock(queue_mutex);
404 error_queue.push_back(resource_load_error(managed->location.name, e));
405 managed->state = ManagedResource::LOAD_ERROR;
408 MutexLock lock(queue_mutex);
409 sync_queue.push_back(async_queue.front());
410 async_queue.pop_front();
411 wait_for_work = async_queue.empty();
414 wait_for_work = true;
418 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(deque<ManagedResource *> &que)
420 MutexLock lock(queue_mutex);
427 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
431 MutexLock lock(queue_mutex);
432 if(r.loader->needs_sync())
433 sync_queue.push_back(&r);
436 bool was_empty = async_queue.empty();
437 async_queue.push_back(&r);
445 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
447 while(!try_remove_resource(r))
448 Time::sleep(Time::msec);
451 if(r.state==ManagedResource::LOADED)
453 MutexLock lock(data_size_mutex);
454 loaded_data_size += r.data_size;
458 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
460 MutexLock lock(queue_mutex);
462 auto i = find(async_queue, &r);
463 if(i==async_queue.end())
465 i = find(sync_queue, &r);
466 if(i!=sync_queue.end())
472 else if(i==async_queue.begin())
476 async_queue.erase(i);
483 bool ResourceManager::LoadingThread::sync()
486 MutexLock lock(queue_mutex);
488 if(!error_queue.empty())
490 resource_load_error err = error_queue.front();
491 error_queue.pop_front();
495 unsigned async_size = async_queue.size();
496 if(async_size==0 && size==capacity)
498 else if(async_size>2 && capacity>2)
502 bool any_finished = false;
503 while(ManagedResource *managed = front(sync_queue))
505 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
507 managed->finish_loading();
508 if(managed->state==ManagedResource::LOADED)
510 MutexLock lock(data_size_mutex);
511 loaded_data_size += managed->data_size;
516 MutexLock lock(queue_mutex);
517 sync_queue.pop_front();
521 MutexLock lock(queue_mutex);
522 bool was_empty = async_queue.empty();
523 async_queue.push_back(sync_queue.front());
524 sync_queue.pop_front();
533 uint64_t ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
535 MutexLock lock(data_size_mutex);
536 uint64_t result = loaded_data_size;
537 loaded_data_size = 0;
541 void ResourceManager::LoadingThread::terminate()