]> git.tdb.fi Git - libs/gl.git/blob - source/resourcemanager.cpp
Rewrite ResourceManager internals for more scalability
[libs/gl.git] / source / resourcemanager.cpp
1 #include <algorithm>
2 #include <msp/time/utils.h>
3 #include "resourcemanager.h"
4 #include "resourcewatcher.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace GL {
10
11 ResourceManager::ResourceManager():
12         policy(LOAD_ON_DEMAND),
13         async_loads(true),
14         size_limit(0),
15         frame(0),
16         min_retain_frames(30),
17         max_retain_frames(0),
18         next_unload(0)
19 { }
20
21 ResourceManager::~ResourceManager()
22 {
23         thread.terminate();
24
25         while(!resources.empty())
26                 resources.begin()->second.resource->set_manager(0);
27 }
28
29 void ResourceManager::set_loading_policy(LoadingPolicy p)
30 {
31         policy = p;
32 }
33
34 void ResourceManager::set_async_loads(bool a)
35 {
36         async_loads = a;
37 }
38
39 void ResourceManager::set_size_limit(UInt64 s)
40 {
41         size_limit = s;
42 }
43
44 void ResourceManager::set_min_retain_frames(unsigned f)
45 {
46         min_retain_frames = f;
47 }
48
49 void ResourceManager::set_max_retain_frames(unsigned f)
50 {
51         max_retain_frames = f;
52 }
53
54 void ResourceManager::add_resource(Resource &r)
55 {
56         insert_unique(resources, &r, ManagedResource(r));
57 }
58
59 void *ResourceManager::get_data_for_resource(const Resource &r)
60 {
61         return &get_item(resources, &r);
62 }
63
64 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
65 {
66         ManagedResource &managed = get_item(resources, &r);
67         managed.collection = &c;
68         managed.name = n;
69
70         if(policy==LOAD_IMMEDIATELY)
71                 load_resource(r);
72 }
73
74 void ResourceManager::load_resource(Resource &r)
75 {
76         ManagedResource &managed = get_item(resources, &r);
77         if(!managed.collection)
78                 throw runtime_error("no location");
79
80         if(managed.state!=ManagedResource::NOT_LOADED)
81                 return;
82
83         if(async_loads)
84         {
85                 managed.state = ManagedResource::LOAD_QUEUED;
86                 queue.push_back(&managed);
87         }
88         else
89         {
90                 managed.start_loading();
91                 while(!managed.loader->process()) ;
92                 managed.finish_loading(true);
93         }
94 }
95
96 void ResourceManager::resource_used(const Resource &r)
97 {
98         ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
99         if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
100                 load_resource(*managed->resource);
101
102         managed->last_used = frame;
103         if(max_retain_frames && !next_unload)
104                 next_unload = frame+max_retain_frames+1;
105 }
106
107 void ResourceManager::remove_resource(Resource &r)
108 {
109         ManagedResource &managed = get_item(resources, &r);
110         ManagedResource::State state = managed.state;
111         if(state==ManagedResource::LOAD_QUEUED)
112         {
113                 LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
114                 if(i!=queue.end())
115                         queue.erase(i);
116         }
117         else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
118                 thread.remove_resource(managed);
119         remove_existing(resources, &r);
120 }
121
122 void ResourceManager::watch_resource(const Resource &r, ResourceWatcher &w)
123 {
124         get_item(resources, &r).add_watcher(w);
125 }
126
127 void ResourceManager::unwatch_resource(const Resource &r, ResourceWatcher &w)
128 {
129         get_item(resources, &r).remove_watcher(w);
130 }
131
132 void ResourceManager::tick()
133 {
134         ++frame;
135
136         bool do_unload = (frame>=next_unload);
137         if(thread.sync())
138                 do_unload = true;
139
140         if(thread.needs_work() && !queue.empty())
141                 dispatch_work();
142
143         if(do_unload)
144         {
145                 if(max_retain_frames && frame>=next_unload)
146                 {
147                         unload_by_age();
148
149                         next_unload = frame;
150                         for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
151                                 if(i->second.state==ManagedResource::LOADED)
152                                         next_unload = min(next_unload, i->second.last_used);
153                         next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
154                 }
155
156                 if(size_limit)
157                         unload_by_size();
158         }
159 }
160
161 void ResourceManager::dispatch_work()
162 {
163         queue.sort(age_order);
164
165         if(queue.front()->last_used+10<frame)
166         {
167                 for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
168                         (*i)->state = ManagedResource::NOT_LOADED;
169                 queue.clear();
170                 return;
171         }
172
173         while(thread.needs_work() && !queue.empty())
174         {
175                 ManagedResource *managed = queue.front();
176                 queue.pop_front();
177                 thread.add_resource(*managed);
178         }
179 }
180
181 void ResourceManager::unload_by_age()
182 {
183         unsigned unload_limit = frame-max_retain_frames;
184
185         for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
186                 if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
187                         i->second.unload();
188 }
189
190 void ResourceManager::unload_by_size()
191 {
192         unsigned unload_limit = frame-min_retain_frames;
193
194         while(get_total_data_size()>size_limit)
195         {
196                 ManagedResource *best = 0;
197                 UInt64 best_impact = 0;
198                 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
199                         if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
200                         {
201                                 UInt64 impact = i->second.data_size*(frame-i->second.last_used);
202                                 if(!best || impact>best_impact)
203                                 {
204                                         best = &i->second;
205                                         best_impact = impact;
206                                 }
207                         }
208
209                 if(!best)
210                         break;
211
212                 best->unload();
213         }
214 }
215
216 UInt64 ResourceManager::get_total_data_size() const
217 {
218         UInt64 total = 0;
219         for(ResourceMap::const_iterator i=resources.begin(); i!=resources.end(); ++i)
220                 if(i->second.state==ManagedResource::LOADED)
221                         total += i->second.data_size;
222         return total;
223 }
224
225 bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
226 {
227         return mr1->last_used>mr2->last_used;
228 }
229
230
231 ResourceManager::ManagedResource::ManagedResource(Resource &r):
232         resource(&r),
233         collection(0),
234         io(0),
235         loader(0),
236         state(NOT_LOADED),
237         last_used(0),
238         data_size(0)
239 { }
240
241 void ResourceManager::ManagedResource::start_loading()
242 {
243         io = collection->open_raw(name);
244         loader = resource->load(*io);
245         if(!loader)
246         {
247                 delete io;
248                 io = 0;
249                 throw logic_error("no loader created");
250         }
251         state = LOADING;
252 }
253
254 bool ResourceManager::ManagedResource::process(bool sync)
255 {
256         while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
257                 if(loader->process())
258                         state = LOAD_FINISHED;
259
260         return state==LOAD_FINISHED;
261 }
262
263 void ResourceManager::ManagedResource::finish_loading(bool successful)
264 {
265         delete loader;
266         loader = 0;
267         delete io;
268         io = 0;
269
270         if(successful)
271         {
272                 state = LOADED;
273                 data_size = resource->get_data_size();
274
275                 for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
276                         (*i)->resource_loaded(*resource);
277         }
278         else
279         {
280                 resource->unload();
281                 state = NOT_LOADED;
282         }
283 }
284
285 void ResourceManager::ManagedResource::finish_loading()
286 {
287         finish_loading(state==LOAD_FINISHED);
288 }
289
290 void ResourceManager::ManagedResource::unload()
291 {
292         resource->unload();
293         state = NOT_LOADED;
294
295         for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
296                 (*i)->resource_unloaded(*resource);
297 }
298
299 void ResourceManager::ManagedResource::add_watcher(ResourceWatcher &w)
300 {
301         if(find(watchers.begin(), watchers.end(), &w)==watchers.end())
302                 watchers.push_back(&w);
303 }
304
305 void ResourceManager::ManagedResource::remove_watcher(ResourceWatcher &w)
306 {
307         vector<ResourceWatcher *>::iterator end = remove(watchers.begin(), watchers.end(), &w);
308         if(end!=watchers.end())
309                 watchers.erase(end, watchers.end());
310 }
311
312
313 ResourceManager::LoadingThread::LoadingThread():
314         sem(1),
315         capacity(2),
316         size(0),
317         done(false)
318 {
319         launch();
320 }
321
322 void ResourceManager::LoadingThread::main()
323 {
324         bool wait_for_work = false;
325         while(!done)
326         {
327                 if(wait_for_work)
328                         sem.wait();
329
330                 if(ManagedResource *managed = front(async_queue))
331                 {
332                         managed->process(false);
333
334                         MutexLock lock(queue_mutex);
335                         sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
336                         wait_for_work = async_queue.empty();
337                 }
338                 else
339                         wait_for_work = true;
340         }
341 }
342
343 ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &queue)
344 {
345         MutexLock lock(queue_mutex);
346         if(queue.empty())
347                 return 0;
348
349         return queue.front();
350 }
351
352 void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
353 {
354         r.start_loading();
355
356         MutexLock lock(queue_mutex);
357         if(r.loader->needs_sync())
358                 sync_queue.push_back(&r);
359         else
360         {
361                 bool was_empty = async_queue.empty();
362                 async_queue.push_back(&r);
363                 if(was_empty)
364                         sem.signal();
365         }
366
367         ++size;
368 }
369
370 void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
371 {
372         while(!try_remove_resource(r))
373                 Time::sleep(Time::msec);
374
375         r.finish_loading();
376 }
377
378 bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
379 {
380         MutexLock lock(queue_mutex);
381
382         LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
383         if(i==async_queue.end())
384         {
385                 i = find(sync_queue.begin(), sync_queue.end(), &r);
386                 if(i!=sync_queue.end())
387                         sync_queue.erase(i);
388         }
389         else if(i==async_queue.begin())
390                 return false;
391         else
392                 async_queue.erase(i);
393
394         return true;
395 }
396
397 bool ResourceManager::LoadingThread::sync()
398 {
399         {
400                 MutexLock lock(queue_mutex);
401                 unsigned async_size = async_queue.size();
402                 if(async_size==0 && size==capacity)
403                         ++capacity;
404                 else if(async_size>2 && capacity>2)
405                         --capacity;
406         }
407
408         bool any_finished = false;
409         while(ManagedResource *managed = front(sync_queue))
410         {
411                 if(managed->process(true))
412                 {
413                         managed->finish_loading();
414                         any_finished = true;
415                         --size;
416
417                         MutexLock lock(queue_mutex);
418                         sync_queue.pop_front();
419                 }
420                 else
421                 {
422                         MutexLock lock(queue_mutex);
423                         bool was_empty = async_queue.empty();
424                         async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
425                         if(was_empty)
426                                 sem.signal();
427                 }
428         }
429
430         return any_finished;
431 }
432
433 void ResourceManager::LoadingThread::terminate()
434 {
435         done = true;
436         sem.signal();
437         join();
438         async_queue.clear();
439         sync_queue.clear();
440 }
441
442 } // namespace GL
443 } // namespace Msp