]> git.tdb.fi Git - libs/gl.git/blob - source/resourcemanager.cpp
Add a ResourceWatcher interface
[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         thread(*this)
20 { }
21
22 ResourceManager::~ResourceManager()
23 {
24         thread.terminate();
25
26         for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
27                 i->second.resource->set_manager(0);
28 }
29
30 void ResourceManager::set_loading_policy(LoadingPolicy p)
31 {
32         policy = p;
33 }
34
35 void ResourceManager::set_async_loads(bool a)
36 {
37         async_loads = a;
38 }
39
40 void ResourceManager::set_size_limit(UInt64 s)
41 {
42         size_limit = s;
43 }
44
45 void ResourceManager::set_min_retain_frames(unsigned f)
46 {
47         min_retain_frames = f;
48 }
49
50 void ResourceManager::set_max_retain_frames(unsigned f)
51 {
52         max_retain_frames = f;
53 }
54
55 void ResourceManager::add_resource(Resource &r)
56 {
57         insert_unique(resources, &r, ManagedResource(r));
58 }
59
60 void *ResourceManager::get_data_for_resource(const Resource &r)
61 {
62         return &get_item(resources, &r);
63 }
64
65 void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
66 {
67         ManagedResource &managed = get_item(resources, &r);
68         managed.collection = &c;
69         managed.name = n;
70
71         if(policy==LOAD_IMMEDIATELY)
72                 load_resource(r);
73 }
74
75 void ResourceManager::load_resource(Resource &r)
76 {
77         ManagedResource &managed = get_item(resources, &r);
78         if(!managed.collection)
79                 throw runtime_error("no location");
80
81         if(managed.loader)
82                 return;
83
84         managed.start_loading();
85
86         if(async_loads)
87                 queue.push_back(&managed);
88         else
89         {
90                 while(!managed.loader->process()) ;
91                 managed.finish_loading();
92         }
93 }
94
95 void ResourceManager::resource_used(const Resource &r)
96 {
97         ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
98         if(!managed->loaded && !managed->loader && policy!=LOAD_MANUALLY)
99                 load_resource(*managed->resource);
100
101         managed->last_used = frame;
102         if(max_retain_frames && !next_unload)
103                 next_unload = frame+max_retain_frames;
104 }
105
106 void ResourceManager::remove_resource(Resource &r)
107 {
108         ManagedResource *loading = thread.get_resource();
109         if(loading && loading->resource==&r)
110                 thread.set_resource(0);
111
112         remove_existing(resources, &r);
113 }
114
115 void ResourceManager::watch_resource(const Resource &r, ResourceWatcher &w)
116 {
117         get_item(resources, &r).add_watcher(w);
118 }
119
120 void ResourceManager::unwatch_resource(const Resource &r, ResourceWatcher &w)
121 {
122         get_item(resources, &r).remove_watcher(w);
123 }
124
125 void ResourceManager::tick()
126 {
127         LoadingThread::State thread_state = thread.get_state();
128         bool check_total_size = false;
129         if(thread_state==LoadingThread::SYNC_PENDING || thread_state==LoadingThread::LOAD_FINISHED)
130         {
131                 thread.sync();
132                 check_total_size = true;
133         }
134         else if(thread_state==LoadingThread::IDLE && !queue.empty())
135         {
136                 ManagedResource *managed = queue.front();
137                 queue.pop_front();
138                 thread.set_resource(managed);
139         }
140
141         ++frame;
142         if(frame==next_unload)
143         {
144                 unsigned unload_limit = frame-max_retain_frames;
145                 next_unload = 0;
146
147                 for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
148                         if(i->second.loaded)
149                         {
150                                 if(i->second.last_used<=unload_limit)
151                                         i->second.unload();
152                                 else if(!next_unload || i->second.last_used<next_unload)
153                                         next_unload = i->second.last_used;
154                         }
155
156                 if(next_unload)
157                         next_unload += max_retain_frames;
158         }
159
160         if(check_total_size)
161         {
162                 while(get_total_data_size()>size_limit)
163                 {
164                         unsigned unload_limit = frame-min_retain_frames;
165                         ManagedResource *best = 0;
166                         UInt64 best_impact = 0;
167                         for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
168                                 if(i->second.loaded && i->second.last_used<unload_limit)
169                                 {
170                                         UInt64 impact = i->second.data_size*(frame-i->second.last_used);
171                                         if(!best || impact>best_impact)
172                                         {
173                                                 best = &i->second;
174                                                 best_impact = impact;
175                                         }
176                                 }
177
178                         if(!best)
179                                 break;
180
181                         best->unload();
182                 }
183         }
184 }
185
186 UInt64 ResourceManager::get_total_data_size() const
187 {
188         UInt64 total = 0;
189         for(ResourceMap::const_iterator i=resources.begin(); i!=resources.end(); ++i)
190                 if(i->second.loaded)
191                         total += i->second.data_size;
192         return total;
193 }
194
195
196 ResourceManager::ManagedResource::ManagedResource(Resource &r):
197         resource(&r),
198         collection(0),
199         io(0),
200         loader(0),
201         loaded(false),
202         last_used(0),
203         data_size(0)
204 { }
205
206 void ResourceManager::ManagedResource::start_loading()
207 {
208         io = collection->open_raw(name);
209         loader = resource->load(*io);
210         if(!loader)
211         {
212                 delete io;
213                 io = 0;
214                 throw logic_error("no loader created");
215         }
216 }
217
218 void ResourceManager::ManagedResource::finish_loading()
219 {
220         delete loader;
221         loader = 0;
222         loaded = true;
223         delete io;
224         io = 0;
225         data_size = resource->get_data_size();
226
227         for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
228                 (*i)->resource_loaded(*resource);
229 }
230
231 void ResourceManager::ManagedResource::unload()
232 {
233         resource->unload();
234         loaded = false;
235
236         for(vector<ResourceWatcher *>::const_iterator i=watchers.begin(); i!=watchers.end(); ++i)
237                 (*i)->resource_unloaded(*resource);
238 }
239
240 void ResourceManager::ManagedResource::add_watcher(ResourceWatcher &w)
241 {
242         if(find(watchers.begin(), watchers.end(), &w)==watchers.end())
243                 watchers.push_back(&w);
244 }
245
246 void ResourceManager::ManagedResource::remove_watcher(ResourceWatcher &w)
247 {
248         vector<ResourceWatcher *>::iterator end = remove(watchers.begin(), watchers.end(), &w);
249         if(end!=watchers.end())
250                 watchers.erase(end, watchers.end());
251 }
252
253
254 ResourceManager::LoadingThread::LoadingThread(ResourceManager &m):
255         manager(m),
256         sem(1),
257         resource(0),
258         state(IDLE)
259 {
260         launch();
261 }
262
263 void ResourceManager::LoadingThread::main()
264 {
265         while(state!=TERMINATING)
266         {
267                 sem.wait();
268
269                 if(state==BUSY)
270                 {
271                         Resource::AsyncLoader *ldr = resource->loader;
272                         bool finished = false;
273                         while(!finished && !ldr->needs_sync())
274                                 finished = ldr->process();
275
276                         if(finished)
277                                 state = LOAD_FINISHED;
278                         else
279                                 state = SYNC_PENDING;
280                 }
281         }
282 }
283
284 void ResourceManager::LoadingThread::set_resource(ManagedResource *r)
285 {
286         if(state!=IDLE)
287         {
288                 while(state==BUSY) ;
289                 // Force finish to clean up the loader
290                 state = LOAD_FINISHED;
291                 sync();
292         }
293
294         resource = r;
295         state = BUSY;
296         sem.signal();
297 }
298
299 void ResourceManager::LoadingThread::sync()
300 {
301         State s = state;
302         bool finished = (s==LOAD_FINISHED);
303         if(s==SYNC_PENDING)
304         {
305                 Resource::AsyncLoader *ldr = resource->loader;
306                 while(!finished && ldr->needs_sync())
307                         finished = ldr->process();
308
309                 if(!finished)
310                 {
311                         state = BUSY;
312                         sem.signal();
313                         return;
314                 }
315         }
316
317         if(finished)
318         {
319                 resource->finish_loading();
320                 resource = 0;
321                 state = IDLE;
322         }
323 }
324
325 void ResourceManager::LoadingThread::terminate()
326 {
327         while(state==BUSY) ;
328         state = TERMINATING;
329         sem.signal();
330         join();
331 }
332
333 } // namespace GL
334 } // namespace Msp