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