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()
28 while(!resources.empty())
29 resources.begin()->second.resource->set_manager(0);
32 void ResourceManager::set_loading_policy(LoadingPolicy p)
37 void ResourceManager::set_async_loads(bool a)
42 void ResourceManager::set_size_limit(uint64_t s)
47 void ResourceManager::set_min_retain_frames(unsigned f)
49 min_retain_frames = f;
52 void ResourceManager::set_max_retain_frames(unsigned f)
54 max_retain_frames = f;
57 void ResourceManager::add_resource(Resource &r)
59 MutexLock lock(map_mutex);
60 insert_unique(resources, &r, ManagedResource(r));
63 const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
65 MutexLock lock(map_mutex);
66 return get_item(resources, &r);
69 ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
71 MutexLock lock(map_mutex);
72 return get_item(resources, &r);
75 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
77 set_resource_location(r, ResourceLocation(c, n));
80 void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
83 MutexLock lock(map_mutex);
84 ManagedResource &managed = get_item(resources, &r);
88 if(policy==LOAD_IMMEDIATELY)
92 const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
94 const ManagedResource &managed = get_managed_resource(r);
95 return managed.location.collection ? &managed.location : 0;
98 void ResourceManager::load_resource(Resource &r)
100 ManagedResource &managed = get_managed_resource(r);
101 if(!managed.location.collection)
102 throw runtime_error("no location");
104 if(managed.state!=ManagedResource::NOT_LOADED)
109 managed.state = ManagedResource::LOAD_QUEUED;
110 auto i = find_if(queue, [&managed](ManagedResource *q){ return q->load_priority>managed.load_priority; });
111 queue.insert(i, &managed);
115 managed.start_loading();
116 while(!managed.loader->process()) ;
117 managed.finish_loading(true);
118 total_data_size += managed.data_size;
122 bool ResourceManager::is_resource_loaded(const Resource &r) const
124 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
125 return managed ? managed->state==ManagedResource::LOADED : false;
128 void ResourceManager::resource_used(const Resource &r)
130 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
133 if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
134 load_resource(*managed->resource);
136 managed->last_used = frame;
137 if(max_retain_frames && !next_unload)
138 next_unload = frame+max_retain_frames+1;
141 void ResourceManager::remove_resource(Resource &r)
143 ManagedResource &managed = get_managed_resource(r);
144 ManagedResource::State state = managed.state;
145 if(state==ManagedResource::LOAD_QUEUED)
147 auto i = find(queue, &managed);
151 else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
152 thread.remove_resource(managed);
154 for(ResourceObserver *o: managed.observers)
155 o->resource_removed(r);
157 MutexLock lock(map_mutex);
158 remove_existing(resources, &r);
161 void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
163 get_managed_resource(r).add_observer(w);
166 void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
168 get_managed_resource(r).remove_observer(w);
171 void ResourceManager::tick()
175 bool do_unload = (frame>=next_unload);
178 total_data_size += thread.get_and_reset_loaded_data_size();
182 if(thread.needs_work() && !queue.empty())
187 MutexLock lock(map_mutex);
188 if(max_retain_frames && frame>=next_unload)
193 for(const auto &kvp: resources)
194 if(kvp.second.state==ManagedResource::LOADED)
195 next_unload = min(next_unload, kvp.second.last_used);
196 next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
204 void ResourceManager::dispatch_work()
206 sort(queue, age_order);
208 if(queue.front()->last_used+min_retain_frames<frame)
210 for(ManagedResource *r: queue)
211 r->state = ManagedResource::NOT_LOADED;
216 while(thread.needs_work() && !queue.empty())
218 ManagedResource *managed = queue.front();
220 thread.add_resource(*managed);
224 void ResourceManager::unload_by_age()
226 unsigned unload_limit = frame-max_retain_frames;
228 for(auto &kvp: resources)
229 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
232 total_data_size -= kvp.second.data_size;
236 void ResourceManager::unload_by_size()
238 unsigned unload_limit = frame-min_retain_frames;
240 while(total_data_size>size_limit)
242 ManagedResource *best = 0;
243 uint64_t best_impact = 0;
244 for(auto &kvp: resources)
245 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
247 uint64_t impact = kvp.second.data_size*(frame-kvp.second.last_used);
248 if(!best || impact>best_impact)
251 best_impact = impact;
259 total_data_size -= best->data_size;
263 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
265 return mr1->last_used>mr2->last_used;
269 ResourceManager::ResourceLocation::ResourceLocation():
273 ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
279 ResourceManager::ManagedResource::ManagedResource(Resource &r):
281 load_priority(r.get_load_priority()),
289 void ResourceManager::ManagedResource::start_loading()
291 io = location.collection->open_raw(location.name);
293 throw resource_load_error(location.name, "open failed");
295 const Resources *res = dynamic_cast<Resources *>(location.collection);
296 loader = resource->load(*io, res);
301 throw logic_error("no loader created");
306 bool ResourceManager::ManagedResource::process(bool sync)
308 while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
309 if(loader->process())
310 state = LOAD_FINISHED;
312 return state==LOAD_FINISHED;
315 void ResourceManager::ManagedResource::finish_loading(bool successful)
325 data_size = resource->get_data_size();
327 for(ResourceObserver *o: observers)
328 o->resource_loaded(*resource);
337 void ResourceManager::ManagedResource::finish_loading()
339 finish_loading(state==LOAD_FINISHED);
342 void ResourceManager::ManagedResource::unload()
347 for(ResourceObserver *o: observers)
348 o->resource_unloaded(*resource);
351 void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
353 if(find(observers.begin(), observers.end(), &w)==observers.end())
354 observers.push_back(&w);
357 void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
359 auto end = remove(observers.begin(), observers.end(), &w);
360 if(end!=observers.end())
361 observers.erase(end, observers.end());
365 ResourceManager::LoadingThread::LoadingThread():
366 Thread("ResourceManager"),
376 void ResourceManager::LoadingThread::main()
378 bool wait_for_work = false;
384 if(ManagedResource *managed = front(async_queue))
388 managed->process(false);
390 catch(const exception &e)
392 MutexLock lock(queue_mutex);
393 error_queue.push_back(resource_load_error(managed->location.name, e));
394 managed->state = ManagedResource::LOAD_ERROR;
397 MutexLock lock(queue_mutex);
398 sync_queue.push_back(async_queue.front());
399 async_queue.pop_front();
400 wait_for_work = async_queue.empty();
403 wait_for_work = true;
407 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(deque<ManagedResource *> &que)
409 MutexLock lock(queue_mutex);
416 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
420 MutexLock lock(queue_mutex);
421 if(r.loader->needs_sync())
422 sync_queue.push_back(&r);
425 bool was_empty = async_queue.empty();
426 async_queue.push_back(&r);
434 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
436 while(!try_remove_resource(r))
437 Time::sleep(Time::msec);
440 if(r.state==ManagedResource::LOADED)
442 MutexLock lock(data_size_mutex);
443 loaded_data_size += r.data_size;
447 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
449 MutexLock lock(queue_mutex);
451 auto i = find(async_queue, &r);
452 if(i==async_queue.end())
454 i = find(sync_queue, &r);
455 if(i!=sync_queue.end())
461 else if(i==async_queue.begin())
465 async_queue.erase(i);
472 bool ResourceManager::LoadingThread::sync()
475 MutexLock lock(queue_mutex);
477 if(!error_queue.empty())
479 resource_load_error err = error_queue.front();
480 error_queue.pop_front();
484 unsigned async_size = async_queue.size();
485 if(async_size==0 && size==capacity)
487 else if(async_size>2 && capacity>2)
491 bool any_finished = false;
492 while(ManagedResource *managed = front(sync_queue))
494 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
496 managed->finish_loading();
497 if(managed->state==ManagedResource::LOADED)
499 MutexLock lock(data_size_mutex);
500 loaded_data_size += managed->data_size;
505 MutexLock lock(queue_mutex);
506 sync_queue.pop_front();
510 MutexLock lock(queue_mutex);
511 bool was_empty = async_queue.empty();
512 async_queue.push_back(sync_queue.front());
513 sync_queue.pop_front();
522 uint64_t ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
524 MutexLock lock(data_size_mutex);
525 uint64_t result = loaded_data_size;
526 loaded_data_size = 0;
530 void ResourceManager::LoadingThread::terminate()