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