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 const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
68 MutexLock lock(map_mutex);
69 return get_item(resources, &r);
72 ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
74 MutexLock lock(map_mutex);
75 return get_item(resources, &r);
78 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
80 set_resource_location(r, ResourceLocation(c, n));
83 void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
86 MutexLock lock(map_mutex);
87 ManagedResource &managed = get_item(resources, &r);
91 if(policy==LOAD_IMMEDIATELY)
95 const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
97 const ManagedResource &managed = get_managed_resource(r);
98 return managed.location.collection ? &managed.location : 0;
101 void ResourceManager::load_resource(Resource &r)
103 ManagedResource &managed = get_managed_resource(r);
104 if(!managed.location.collection)
105 throw runtime_error("no location");
107 if(managed.state!=ManagedResource::NOT_LOADED)
112 managed.state = ManagedResource::LOAD_QUEUED;
113 auto i = find_if(queue, [&managed](ManagedResource *q){ return q->load_priority>managed.load_priority; });
114 queue.insert(i, &managed);
118 managed.start_loading();
119 while(!managed.loader->process()) ;
120 managed.finish_loading(true);
121 total_data_size += managed.data_size;
125 bool ResourceManager::is_resource_loaded(const Resource &r) const
127 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
128 return managed ? managed->state==ManagedResource::LOADED : false;
131 void ResourceManager::resource_used(const Resource &r)
133 ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
136 if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
137 load_resource(*managed->resource);
139 managed->last_used = frame;
140 if(max_retain_frames && !next_unload)
141 next_unload = frame+max_retain_frames+1;
144 void ResourceManager::remove_resource(Resource &r)
146 ManagedResource &managed = get_managed_resource(r);
147 ManagedResource::State state = managed.state;
148 if(state==ManagedResource::LOAD_QUEUED)
150 auto i = find(queue, &managed);
154 else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
155 thread.remove_resource(managed);
157 for(ResourceObserver *o: managed.observers)
158 o->resource_removed(r);
160 MutexLock lock(map_mutex);
161 remove_existing(resources, &r);
164 void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
166 get_managed_resource(r).add_observer(w);
169 void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
171 get_managed_resource(r).remove_observer(w);
174 void ResourceManager::tick()
178 bool do_unload = (frame>=next_unload);
181 total_data_size += thread.get_and_reset_loaded_data_size();
185 if(thread.needs_work() && !queue.empty())
190 MutexLock lock(map_mutex);
191 if(max_retain_frames && frame>=next_unload)
196 for(const auto &kvp: resources)
197 if(kvp.second.state==ManagedResource::LOADED)
198 next_unload = min(next_unload, kvp.second.last_used);
199 next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
207 void ResourceManager::dispatch_work()
209 sort(queue, age_order);
211 if(queue.front()->last_used+min_retain_frames<frame)
213 for(ManagedResource *r: queue)
214 r->state = ManagedResource::NOT_LOADED;
219 while(thread.needs_work() && !queue.empty())
221 ManagedResource *managed = queue.front();
223 thread.add_resource(*managed);
227 void ResourceManager::unload_by_age()
229 unsigned unload_limit = frame-max_retain_frames;
231 for(auto &kvp: resources)
232 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
235 total_data_size -= kvp.second.data_size;
239 void ResourceManager::unload_by_size()
241 unsigned unload_limit = frame-min_retain_frames;
243 while(total_data_size>size_limit)
245 ManagedResource *best = 0;
246 uint64_t best_impact = 0;
247 for(auto &kvp: resources)
248 if(kvp.second.state==ManagedResource::LOADED && kvp.second.last_used<unload_limit)
250 uint64_t impact = kvp.second.data_size*(frame-kvp.second.last_used);
251 if(!best || impact>best_impact)
254 best_impact = impact;
262 total_data_size -= best->data_size;
266 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
268 return mr1->last_used>mr2->last_used;
272 ResourceManager::ResourceLocation::ResourceLocation():
276 ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
282 ResourceManager::ManagedResource::ManagedResource(Resource &r):
284 load_priority(r.get_load_priority()),
292 void ResourceManager::ManagedResource::start_loading()
294 io = location.collection->open_raw(location.name);
296 throw resource_load_error(location.name, "open failed");
298 const Resources *res = dynamic_cast<Resources *>(location.collection);
299 loader = resource->load(*io, res);
304 throw logic_error("no loader created");
309 bool ResourceManager::ManagedResource::process(bool sync)
311 while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
312 if(loader->process())
313 state = LOAD_FINISHED;
315 return state==LOAD_FINISHED;
318 void ResourceManager::ManagedResource::finish_loading(bool successful)
328 data_size = resource->get_data_size();
330 for(ResourceObserver *o: observers)
331 o->resource_loaded(*resource);
340 void ResourceManager::ManagedResource::finish_loading()
342 finish_loading(state==LOAD_FINISHED);
345 void ResourceManager::ManagedResource::unload()
350 for(ResourceObserver *o: observers)
351 o->resource_unloaded(*resource);
354 void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
356 if(find(observers.begin(), observers.end(), &w)==observers.end())
357 observers.push_back(&w);
360 void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
362 auto end = remove(observers.begin(), observers.end(), &w);
363 if(end!=observers.end())
364 observers.erase(end, observers.end());
368 ResourceManager::LoadingThread::LoadingThread():
369 Thread("ResourceManager"),
379 void ResourceManager::LoadingThread::main()
381 bool wait_for_work = false;
387 if(ManagedResource *managed = front(async_queue))
391 managed->process(false);
393 catch(const exception &e)
395 MutexLock lock(queue_mutex);
396 error_queue.push_back(resource_load_error(managed->location.name, e));
397 managed->state = ManagedResource::LOAD_ERROR;
400 MutexLock lock(queue_mutex);
401 sync_queue.push_back(async_queue.front());
402 async_queue.pop_front();
403 wait_for_work = async_queue.empty();
406 wait_for_work = true;
410 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(deque<ManagedResource *> &que)
412 MutexLock lock(queue_mutex);
419 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
423 MutexLock lock(queue_mutex);
424 if(r.loader->needs_sync())
425 sync_queue.push_back(&r);
428 bool was_empty = async_queue.empty();
429 async_queue.push_back(&r);
437 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
439 while(!try_remove_resource(r))
440 Time::sleep(Time::msec);
443 if(r.state==ManagedResource::LOADED)
445 MutexLock lock(data_size_mutex);
446 loaded_data_size += r.data_size;
450 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
452 MutexLock lock(queue_mutex);
454 auto i = find(async_queue, &r);
455 if(i==async_queue.end())
457 i = find(sync_queue, &r);
458 if(i!=sync_queue.end())
464 else if(i==async_queue.begin())
468 async_queue.erase(i);
475 bool ResourceManager::LoadingThread::sync()
478 MutexLock lock(queue_mutex);
480 if(!error_queue.empty())
482 resource_load_error err = error_queue.front();
483 error_queue.pop_front();
487 unsigned async_size = async_queue.size();
488 if(async_size==0 && size==capacity)
490 else if(async_size>2 && capacity>2)
494 bool any_finished = false;
495 while(ManagedResource *managed = front(sync_queue))
497 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
499 managed->finish_loading();
500 if(managed->state==ManagedResource::LOADED)
502 MutexLock lock(data_size_mutex);
503 loaded_data_size += managed->data_size;
508 MutexLock lock(queue_mutex);
509 sync_queue.pop_front();
513 MutexLock lock(queue_mutex);
514 bool was_empty = async_queue.empty();
515 async_queue.push_back(sync_queue.front());
516 sync_queue.pop_front();
525 uint64_t ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
527 MutexLock lock(data_size_mutex);
528 uint64_t result = loaded_data_size;
529 loaded_data_size = 0;
533 void ResourceManager::LoadingThread::terminate()