]> git.tdb.fi Git - libs/gl.git/blob - source/resourcemanager.cpp
Move transform loading to ObjectInstance
[libs/gl.git] / source / resourcemanager.cpp
1 #include <algorithm>
2 #include <typeinfo>
3 #include <msp/debug/demangle.h>
4 #include <msp/strings/format.h>
5 #include <msp/time/utils.h>
6 #include "resourcemanager.h"
7 #include "resources.h"
8 #include "resourceobserver.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 resource_load_error::resource_load_error(const string &name, const string &err):
16         runtime_error(format("%s: %s", name, err))
17 { }
18
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()))
21 { }
22
23
24 ResourceManager::ResourceManager():
25         policy(LOAD_ON_DEMAND),
26         async_loads(true),
27         total_data_size(0),
28         size_limit(0),
29         frame(0),
30         min_retain_frames(30),
31         max_retain_frames(0),
32         next_unload(0)
33 { }
34
35 ResourceManager::~ResourceManager()
36 {
37         thread.terminate();
38
39         while(!resources.empty())
40                 resources.begin()->second.resource->set_manager(0);
41 }
42
43 void ResourceManager::set_loading_policy(LoadingPolicy p)
44 {
45         policy = p;
46 }
47
48 void ResourceManager::set_async_loads(bool a)
49 {
50         async_loads = a;
51 }
52
53 void ResourceManager::set_size_limit(UInt64 s)
54 {
55         size_limit = s;
56 }
57
58 void ResourceManager::set_min_retain_frames(unsigned f)
59 {
60         min_retain_frames = f;
61 }
62
63 void ResourceManager::set_max_retain_frames(unsigned f)
64 {
65         max_retain_frames = f;
66 }
67
68 void ResourceManager::add_resource(Resource &r)
69 {
70         MutexLock lock(map_mutex);
71         insert_unique(resources, &r, ManagedResource(r));
72 }
73
74 const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
75 {
76         MutexLock lock(map_mutex);
77         return get_item(resources, &r);
78 }
79
80 ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
81 {
82         MutexLock lock(map_mutex);
83         return get_item(resources, &r);
84 }
85
86 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
87 {
88         set_resource_location(r, ResourceLocation(c, n));
89 }
90
91 void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
92 {
93         {
94                 MutexLock lock(map_mutex);
95                 ManagedResource &managed = get_item(resources, &r);
96                 managed.location = l;
97         }
98
99         if(policy==LOAD_IMMEDIATELY)
100                 load_resource(r);
101 }
102
103 const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
104 {
105         const ManagedResource &managed = get_managed_resource(r);
106         return managed.location.collection ? &managed.location : 0;
107 }
108
109 void ResourceManager::load_resource(Resource &r)
110 {
111         ManagedResource &managed = get_managed_resource(r);
112         if(!managed.location.collection)
113                 throw runtime_error("no location");
114
115         if(managed.state!=ManagedResource::NOT_LOADED)
116                 return;
117
118         if(async_loads)
119         {
120                 managed.state = ManagedResource::LOAD_QUEUED;
121                 LoadQueue::iterator i;
122                 for(i=queue.begin(); (i!=queue.end() && (*i)->load_priority>=managed.load_priority); ++i) ;
123                 queue.insert(i, &managed);
124         }
125         else
126         {
127                 managed.start_loading();
128                 while(!managed.loader->process()) ;
129                 managed.finish_loading(true);
130                 total_data_size += managed.data_size;
131         }
132 }
133
134 bool ResourceManager::is_resource_loaded(const Resource &r) const
135 {
136         ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
137         return managed ? managed->state==ManagedResource::LOADED : false;
138 }
139
140 void ResourceManager::resource_used(const Resource &r)
141 {
142         ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
143         if(!managed)
144                 return;
145         if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
146                 load_resource(*managed->resource);
147
148         managed->last_used = frame;
149         if(max_retain_frames && !next_unload)
150                 next_unload = frame+max_retain_frames+1;
151 }
152
153 void ResourceManager::remove_resource(Resource &r)
154 {
155         ManagedResource &managed = get_managed_resource(r);
156         ManagedResource::State state = managed.state;
157         if(state==ManagedResource::LOAD_QUEUED)
158         {
159                 LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
160                 if(i!=queue.end())
161                         queue.erase(i);
162         }
163         else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
164                 thread.remove_resource(managed);
165
166         for(vector<ResourceObserver *>::const_iterator i=managed.observers.begin(); i!=managed.observers.end(); ++i)
167                 (*i)->resource_removed(r);
168
169         MutexLock lock(map_mutex);
170         remove_existing(resources, &r);
171 }
172
173 void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
174 {
175         get_managed_resource(r).add_observer(w);
176 }
177
178 void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
179 {
180         get_managed_resource(r).remove_observer(w);
181 }
182
183 void ResourceManager::tick()
184 {
185         ++frame;
186
187         bool do_unload = (frame>=next_unload);
188         if(thread.sync())
189         {
190                 total_data_size += thread.get_and_reset_loaded_data_size();
191                 do_unload = true;
192         }
193
194         if(thread.needs_work() && !queue.empty())
195                 dispatch_work();
196
197         if(do_unload)
198         {
199                 MutexLock lock(map_mutex);
200                 if(max_retain_frames && frame>=next_unload)
201                 {
202                         unload_by_age();
203
204                         next_unload = frame;
205                         for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
206                                 if(i->second.state==ManagedResource::LOADED)
207                                         next_unload = min(next_unload, i->second.last_used);
208                         next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
209                 }
210
211                 if(size_limit)
212                         unload_by_size();
213         }
214 }
215
216 void ResourceManager::dispatch_work()
217 {
218         queue.sort(age_order);
219
220         if(queue.front()->last_used+10<frame)
221         {
222                 for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
223                         (*i)->state = ManagedResource::NOT_LOADED;
224                 queue.clear();
225                 return;
226         }
227
228         while(thread.needs_work() && !queue.empty())
229         {
230                 ManagedResource *managed = queue.front();
231                 queue.pop_front();
232                 thread.add_resource(*managed);
233         }
234 }
235
236 void ResourceManager::unload_by_age()
237 {
238         unsigned unload_limit = frame-max_retain_frames;
239
240         for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
241                 if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
242                 {
243                         i->second.unload();
244                         total_data_size -= i->second.data_size;
245                 }
246 }
247
248 void ResourceManager::unload_by_size()
249 {
250         unsigned unload_limit = frame-min_retain_frames;
251
252         while(total_data_size>size_limit)
253         {
254                 ManagedResource *best = 0;
255                 UInt64 best_impact = 0;
256                 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
257                         if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
258                         {
259                                 UInt64 impact = i->second.data_size*(frame-i->second.last_used);
260                                 if(!best || impact>best_impact)
261                                 {
262                                         best = &i->second;
263                                         best_impact = impact;
264                                 }
265                         }
266
267                 if(!best)
268                         break;
269
270                 best->unload();
271                 total_data_size -= best->data_size;
272         }
273 }
274
275 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
276 {
277         return mr1->last_used>mr2->last_used;
278 }
279
280
281 ResourceManager::ResourceLocation::ResourceLocation():
282         collection(0)
283 { }
284
285 ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
286         collection(&c),
287         name(n)
288 { }
289
290
291 ResourceManager::ManagedResource::ManagedResource(Resource &r):
292         resource(&r),
293         load_priority(r.get_load_priority()),
294         io(0),
295         loader(0),
296         state(NOT_LOADED),
297         last_used(0),
298         data_size(0)
299 { }
300
301 void ResourceManager::ManagedResource::start_loading()
302 {
303         io = location.collection->open_raw(location.name);
304         if(!io)
305                 throw resource_load_error(location.name, "open failed");
306
307         const Resources *res = dynamic_cast<Resources *>(location.collection);
308         loader = resource->load(*io, res);
309         if(!loader)
310         {
311                 delete io;
312                 io = 0;
313                 throw logic_error("no loader created");
314         }
315         state = LOADING;
316 }
317
318 bool ResourceManager::ManagedResource::process(bool sync)
319 {
320         while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
321                 if(loader->process())
322                         state = LOAD_FINISHED;
323
324         return state==LOAD_FINISHED;
325 }
326
327 void ResourceManager::ManagedResource::finish_loading(bool successful)
328 {
329         delete loader;
330         loader = 0;
331         delete io;
332         io = 0;
333
334         if(successful)
335         {
336                 state = LOADED;
337                 data_size = resource->get_data_size();
338
339                 for(vector<ResourceObserver *>::const_iterator i=observers.begin(); i!=observers.end(); ++i)
340                         (*i)->resource_loaded(*resource);
341         }
342         else
343         {
344                 resource->unload();
345                 state = NOT_LOADED;
346         }
347 }
348
349 void ResourceManager::ManagedResource::finish_loading()
350 {
351         finish_loading(state==LOAD_FINISHED);
352 }
353
354 void ResourceManager::ManagedResource::unload()
355 {
356         resource->unload();
357         state = NOT_LOADED;
358
359         for(vector<ResourceObserver *>::const_iterator i=observers.begin(); i!=observers.end(); ++i)
360                 (*i)->resource_unloaded(*resource);
361 }
362
363 void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
364 {
365         if(find(observers.begin(), observers.end(), &w)==observers.end())
366                 observers.push_back(&w);
367 }
368
369 void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
370 {
371         vector<ResourceObserver *>::iterator end = remove(observers.begin(), observers.end(), &w);
372         if(end!=observers.end())
373                 observers.erase(end, observers.end());
374 }
375
376
377 ResourceManager::LoadingThread::LoadingThread():
378         sem(1),
379         capacity(2),
380         size(0),
381         loaded_data_size(0),
382         done(false)
383 {
384         launch();
385 }
386
387 void ResourceManager::LoadingThread::main()
388 {
389         bool wait_for_work = false;
390         while(!done)
391         {
392                 if(wait_for_work)
393                         sem.wait();
394
395                 if(ManagedResource *managed = front(async_queue))
396                 {
397                         try
398                         {
399                                 managed->process(false);
400                         }
401                         catch(const exception &e)
402                         {
403                                 MutexLock lock(queue_mutex);
404                                 error_queue.push_back(resource_load_error(managed->location.name, e));
405                                 managed->state = ManagedResource::LOAD_ERROR;
406                         }
407
408                         MutexLock lock(queue_mutex);
409                         sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
410                         wait_for_work = async_queue.empty();
411                 }
412                 else
413                         wait_for_work = true;
414         }
415 }
416
417 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &que)
418 {
419         MutexLock lock(queue_mutex);
420         if(que.empty())
421                 return 0;
422
423         return que.front();
424 }
425
426 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
427 {
428         r.start_loading();
429
430         MutexLock lock(queue_mutex);
431         if(r.loader->needs_sync())
432                 sync_queue.push_back(&r);
433         else
434         {
435                 bool was_empty = async_queue.empty();
436                 async_queue.push_back(&r);
437                 if(was_empty)
438                         sem.signal();
439         }
440
441         ++size;
442 }
443
444 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
445 {
446         while(!try_remove_resource(r))
447                 Time::sleep(Time::msec);
448
449         r.finish_loading();
450         if(r.state==ManagedResource::LOADED)
451         {
452                 MutexLock lock(data_size_mutex);
453                 loaded_data_size += r.data_size;
454         }
455 }
456
457 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
458 {
459         MutexLock lock(queue_mutex);
460
461         LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
462         if(i==async_queue.end())
463         {
464                 i = find(sync_queue.begin(), sync_queue.end(), &r);
465                 if(i!=sync_queue.end())
466                 {
467                         sync_queue.erase(i);
468                         --size;
469                 }
470         }
471         else if(i==async_queue.begin())
472                 return false;
473         else
474         {
475                 async_queue.erase(i);
476                 --size;
477         }
478
479         return true;
480 }
481
482 bool ResourceManager::LoadingThread::sync()
483 {
484         {
485                 MutexLock lock(queue_mutex);
486
487                 if(!error_queue.empty())
488                 {
489                         resource_load_error err = error_queue.front();
490                         error_queue.pop_front();
491                         throw err;
492                 }
493
494                 unsigned async_size = async_queue.size();
495                 if(async_size==0 && size==capacity)
496                         ++capacity;
497                 else if(async_size>2 && capacity>2)
498                         --capacity;
499         }
500
501         bool any_finished = false;
502         while(ManagedResource *managed = front(sync_queue))
503         {
504                 if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
505                 {
506                         managed->finish_loading();
507                         if(managed->state==ManagedResource::LOADED)
508                         {
509                                 MutexLock lock(data_size_mutex);
510                                 loaded_data_size += managed->data_size;
511                         }
512                         any_finished = true;
513                         --size;
514
515                         MutexLock lock(queue_mutex);
516                         sync_queue.pop_front();
517                 }
518                 else
519                 {
520                         MutexLock lock(queue_mutex);
521                         bool was_empty = async_queue.empty();
522                         async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
523                         if(was_empty)
524                                 sem.signal();
525                 }
526         }
527
528         return any_finished;
529 }
530
531 UInt64 ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
532 {
533         MutexLock lock(data_size_mutex);
534         UInt64 result = loaded_data_size;
535         loaded_data_size = 0;
536         return result;
537 }
538
539 void ResourceManager::LoadingThread::terminate()
540 {
541         done = true;
542         sem.signal();
543         join();
544         async_queue.clear();
545         sync_queue.clear();
546 }
547
548 } // namespace GL
549 } // namespace Msp