1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4     (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreStableHeaders.h"
29 #include "OgreResourceManager.h"
30 
31 #include "OgreException.h"
32 #include "OgreArchive.h"
33 #include "OgreArchiveManager.h"
34 #include "OgreStringVector.h"
35 #include "OgreStringConverter.h"
36 #include "OgreResourceGroupManager.h"
37 
38 namespace Ogre {
39 
40     //-----------------------------------------------------------------------
ResourceManager()41     ResourceManager::ResourceManager()
42 		: mNextHandle(1), mMemoryUsage(0), mVerbose(true), mLoadOrder(0)
43     {
44         // Init memory limit & usage
45         mMemoryBudget = std::numeric_limits<unsigned long>::max();
46     }
47     //-----------------------------------------------------------------------
~ResourceManager()48     ResourceManager::~ResourceManager()
49     {
50 		destroyAllResourcePools();
51         removeAll();
52     }
53 	//-----------------------------------------------------------------------
createResource(const String & name,const String & group,bool isManual,ManualResourceLoader * loader,const NameValuePairList * params)54 	ResourcePtr ResourceManager::createResource(const String& name, const String& group,
55 		bool isManual, ManualResourceLoader* loader, const NameValuePairList* params)
56 	{
57 		// Call creation implementation
58 		ResourcePtr ret = ResourcePtr(
59             createImpl(name, getNextHandle(), group, isManual, loader, params));
60         if (params)
61             ret->setParameterList(*params);
62 
63 		addImpl(ret);
64 		// Tell resource group manager
65 		ResourceGroupManager::getSingleton()._notifyResourceCreated(ret);
66 		return ret;
67 
68 	}
69     //-----------------------------------------------------------------------
70     ResourceManager::ResourceCreateOrRetrieveResult
createOrRetrieve(const String & name,const String & group,bool isManual,ManualResourceLoader * loader,const NameValuePairList * params)71 	ResourceManager::createOrRetrieve(
72 		const String& name, const String& group,
73 		bool isManual, ManualResourceLoader* loader,
74 		const NameValuePairList* params)
75 	{
76 		// Lock for the whole get / insert
77             OGRE_LOCK_AUTO_MUTEX;
78 
79 		ResourcePtr res = getResourceByName(name, group);
80 		bool created = false;
81 		if (res.isNull())
82 		{
83 			created = true;
84 			res = createResource(name, group, isManual, loader, params);
85 		}
86 
87 		return ResourceCreateOrRetrieveResult(res, created);
88 	}
89     //-----------------------------------------------------------------------
prepare(const String & name,const String & group,bool isManual,ManualResourceLoader * loader,const NameValuePairList * loadParams,bool backgroundThread)90     ResourcePtr ResourceManager::prepare(const String& name,
91         const String& group, bool isManual, ManualResourceLoader* loader,
92         const NameValuePairList* loadParams, bool backgroundThread)
93     {
94         ResourcePtr r = createOrRetrieve(name,group,isManual,loader,loadParams).first;
95 		// ensure prepared
96         r->prepare(backgroundThread);
97         return r;
98     }
99     //-----------------------------------------------------------------------
load(const String & name,const String & group,bool isManual,ManualResourceLoader * loader,const NameValuePairList * loadParams,bool backgroundThread)100     ResourcePtr ResourceManager::load(const String& name,
101         const String& group, bool isManual, ManualResourceLoader* loader,
102         const NameValuePairList* loadParams, bool backgroundThread)
103     {
104         ResourcePtr r = createOrRetrieve(name,group,isManual,loader,loadParams).first;
105 		// ensure loaded
106         r->load(backgroundThread);
107 
108         return r;
109     }
110     //-----------------------------------------------------------------------
addImpl(ResourcePtr & res)111 	void ResourceManager::addImpl( ResourcePtr& res )
112 	{
113             OGRE_LOCK_AUTO_MUTEX;
114 
115 			std::pair<ResourceMap::iterator, bool> result;
116 		if(ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(res->getGroup()))
117 		{
118 			result = mResources.insert( ResourceMap::value_type( res->getName(), res ) );
119 		}
120 		else
121 		{
122 			ResourceWithGroupMap::iterator itGroup = mResourcesWithGroup.find(res->getGroup());
123 
124 			// we will create the group if it doesn't exists in our list
125 			if( itGroup == mResourcesWithGroup.end())
126 			{
127 				ResourceMap dummy;
128 				mResourcesWithGroup.insert( ResourceWithGroupMap::value_type( res->getGroup(), dummy ) );
129 				itGroup = mResourcesWithGroup.find(res->getGroup());
130 			}
131 			result = itGroup->second.insert( ResourceMap::value_type( res->getName(), res ) );
132 
133 		}
134 
135 		if (!result.second)
136 		{
137 			// Attempt to resolve the collision
138 			if(ResourceGroupManager::getSingleton().getLoadingListener())
139 			{
140 				if(ResourceGroupManager::getSingleton().getLoadingListener()->resourceCollision(res.get(), this))
141 				{
142 					// Try to do the addition again, no seconds attempts to resolve collisions are allowed
143 					std::pair<ResourceMap::iterator, bool> insertResult;
144 					if(ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(res->getGroup()))
145 					{
146 						insertResult = mResources.insert( ResourceMap::value_type( res->getName(), res ) );
147 					}
148 					else
149 					{
150 						ResourceWithGroupMap::iterator itGroup = mResourcesWithGroup.find(res->getGroup());
151 						insertResult = itGroup->second.insert( ResourceMap::value_type( res->getName(), res ) );
152 					}
153 					if (!insertResult.second)
154 					{
155 						OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Resource with the name " + res->getName() +
156 							" already exists.", "ResourceManager::add");
157 					}
158 
159 					std::pair<ResourceHandleMap::iterator, bool> resultHandle =
160 						mResourcesByHandle.insert( ResourceHandleMap::value_type( res->getHandle(), res ) );
161 					if (!resultHandle.second)
162 					{
163 						OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Resource with the handle " +
164 							StringConverter::toString((long) (res->getHandle())) +
165 							" already exists.", "ResourceManager::add");
166 					}
167 				}
168 			}
169 		}
170 		else
171 		{
172 			// Insert the handle
173 			std::pair<ResourceHandleMap::iterator, bool> resultHandle =
174 				mResourcesByHandle.insert( ResourceHandleMap::value_type( res->getHandle(), res ) );
175 			if (!resultHandle.second)
176 			{
177 				OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Resource with the handle " +
178 					StringConverter::toString((long) (res->getHandle())) +
179 					" already exists.", "ResourceManager::add");
180 			}
181 		}
182 	}
183 	//-----------------------------------------------------------------------
removeImpl(ResourcePtr & res)184 	void ResourceManager::removeImpl( ResourcePtr& res )
185 	{
186             OGRE_LOCK_AUTO_MUTEX;
187 
188 		if(ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(res->getGroup()))
189 		{
190 			ResourceMap::iterator nameIt = mResources.find(res->getName());
191 			if (nameIt != mResources.end())
192 			{
193 				mResources.erase(nameIt);
194 			}
195 		}
196 		else
197 		{
198 			ResourceWithGroupMap::iterator groupIt = mResourcesWithGroup.find(res->getGroup());
199 			if (groupIt != mResourcesWithGroup.end())
200 			{
201 				ResourceMap::iterator nameIt = groupIt->second.find(res->getName());
202 				if (nameIt != groupIt->second.end())
203 				{
204 					groupIt->second.erase(nameIt);
205 				}
206 
207 				if (groupIt->second.empty())
208 				{
209 					mResourcesWithGroup.erase(groupIt);
210 				}
211 			}
212 		}
213 
214 		ResourceHandleMap::iterator handleIt = mResourcesByHandle.find(res->getHandle());
215 		if (handleIt != mResourcesByHandle.end())
216 		{
217 			mResourcesByHandle.erase(handleIt);
218 		}
219 		// Tell resource group manager
220 		ResourceGroupManager::getSingleton()._notifyResourceRemoved(res);
221 	}
222     //-----------------------------------------------------------------------
setMemoryBudget(size_t bytes)223     void ResourceManager::setMemoryBudget( size_t bytes)
224     {
225         // Update limit & check usage
226         mMemoryBudget = bytes;
227         checkUsage();
228     }
229     //-----------------------------------------------------------------------
getMemoryBudget(void) const230     size_t ResourceManager::getMemoryBudget(void) const
231     {
232         return mMemoryBudget;
233     }
234 	//-----------------------------------------------------------------------
unload(const String & name)235 	void ResourceManager::unload(const String& name)
236 	{
237 		ResourcePtr res = getResourceByName(name);
238 
239 		if (!res.isNull())
240 		{
241 			// Unload resource
242 			res->unload();
243 
244 		}
245 	}
246 	//-----------------------------------------------------------------------
unload(ResourceHandle handle)247 	void ResourceManager::unload(ResourceHandle handle)
248 	{
249 		ResourcePtr res = getByHandle(handle);
250 
251 		if (!res.isNull())
252 		{
253 			// Unload resource
254 			res->unload();
255 
256 		}
257 	}
258 	//-----------------------------------------------------------------------
unloadAll(bool reloadableOnly)259 	void ResourceManager::unloadAll(bool reloadableOnly)
260 	{
261             OGRE_LOCK_AUTO_MUTEX;
262 
263 		ResourceMap::iterator i, iend;
264 		iend = mResources.end();
265 		for (i = mResources.begin(); i != iend; ++i)
266 		{
267 			if (!reloadableOnly || i->second->isReloadable())
268 			{
269 				i->second->unload();
270 			}
271 		}
272 
273 	}
274 	//-----------------------------------------------------------------------
reloadAll(bool reloadableOnly)275 	void ResourceManager::reloadAll(bool reloadableOnly)
276 	{
277             OGRE_LOCK_AUTO_MUTEX;
278 
279 		ResourceMap::iterator i, iend;
280 		iend = mResources.end();
281 		for (i = mResources.begin(); i != iend; ++i)
282 		{
283 			if (!reloadableOnly || i->second->isReloadable())
284 			{
285 				i->second->reload();
286 			}
287 		}
288 
289 	}
290     //-----------------------------------------------------------------------
unloadUnreferencedResources(bool reloadableOnly)291     void ResourceManager::unloadUnreferencedResources(bool reloadableOnly)
292     {
293         OGRE_LOCK_AUTO_MUTEX;
294 
295         ResourceMap::iterator i, iend;
296         iend = mResources.end();
297         for (i = mResources.begin(); i != iend; ++i)
298         {
299             // A use count of 3 means that only RGM and RM have references
300             // RGM has one (this one) and RM has 2 (by name and by handle)
301             if (i->second.useCount() == ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS)
302             {
303                 Resource* res = i->second.get();
304                 if (!reloadableOnly || res->isReloadable())
305                 {
306                     res->unload();
307                 }
308             }
309         }
310     }
311     //-----------------------------------------------------------------------
reloadUnreferencedResources(bool reloadableOnly)312     void ResourceManager::reloadUnreferencedResources(bool reloadableOnly)
313     {
314         OGRE_LOCK_AUTO_MUTEX;
315 
316         ResourceMap::iterator i, iend;
317         iend = mResources.end();
318         for (i = mResources.begin(); i != iend; ++i)
319         {
320             // A use count of 3 means that only RGM and RM have references
321             // RGM has one (this one) and RM has 2 (by name and by handle)
322             if (i->second.useCount() == ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS)
323             {
324                 Resource* res = i->second.get();
325                 if (!reloadableOnly || res->isReloadable())
326                 {
327                     res->reload();
328                 }
329             }
330         }
331     }
332     //-----------------------------------------------------------------------
remove(ResourcePtr & res)333     void ResourceManager::remove(ResourcePtr& res)
334     {
335         removeImpl(res);
336     }
337 	//-----------------------------------------------------------------------
remove(const String & name)338 	void ResourceManager::remove(const String& name)
339 	{
340 		ResourcePtr res = getResourceByName(name);
341 
342 		if (!res.isNull())
343 		{
344 			removeImpl(res);
345 		}
346 	}
347 	//-----------------------------------------------------------------------
remove(ResourceHandle handle)348 	void ResourceManager::remove(ResourceHandle handle)
349 	{
350 		ResourcePtr res = getByHandle(handle);
351 
352 		if (!res.isNull())
353 		{
354 			removeImpl(res);
355 		}
356 	}
357 	//-----------------------------------------------------------------------
removeAll(void)358 	void ResourceManager::removeAll(void)
359 	{
360             OGRE_LOCK_AUTO_MUTEX;
361 
362 		mResources.clear();
363 		mResourcesWithGroup.clear();
364 		mResourcesByHandle.clear();
365 		// Notify resource group manager
366 		ResourceGroupManager::getSingleton()._notifyAllResourcesRemoved(this);
367 	}
368     //-----------------------------------------------------------------------
removeUnreferencedResources(bool reloadableOnly)369     void ResourceManager::removeUnreferencedResources(bool reloadableOnly)
370     {
371         OGRE_LOCK_AUTO_MUTEX;
372 
373         ResourceMap::iterator i, iend;
374         iend = mResources.end();
375         for (i = mResources.begin(); i != iend; )
376         {
377             // A use count of 3 means that only RGM and RM have references
378             // RGM has one (this one) and RM has 2 (by name and by handle)
379             if (i->second.useCount() == ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS)
380             {
381                 Resource* res = (i++)->second.get();
382                 if (!reloadableOnly || res->isReloadable())
383                 {
384                     remove( res->getHandle() );
385                 }
386             }
387             else
388             {
389                 ++i;
390             }
391         }
392     }
393     //-----------------------------------------------------------------------
getResourceByName(const String & name,const String & groupName)394     ResourcePtr ResourceManager::getResourceByName(const String& name, const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */)
395     {
396 		ResourcePtr res;
397 
398 		// if not in the global pool - get it from the grouped pool
399 		if(!ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(groupName))
400 		{
401                     OGRE_LOCK_AUTO_MUTEX;
402 			ResourceWithGroupMap::iterator itGroup = mResourcesWithGroup.find(groupName);
403 
404 			if( itGroup != mResourcesWithGroup.end())
405 			{
406 				ResourceMap::iterator it = itGroup->second.find(name);
407 
408 				if( it != itGroup->second.end())
409 				{
410 					res = it->second;
411 				}
412 			}
413 		}
414 
415 		// if didn't find it the grouped pool - get it from the global pool
416 		if (res.isNull())
417 		{
418                     OGRE_LOCK_AUTO_MUTEX;
419 
420 			ResourceMap::iterator it = mResources.find(name);
421 
422 			if( it != mResources.end())
423 			{
424 				res = it->second;
425 			}
426 			else
427 			{
428 				// this is the case when we need to search also in the grouped hash
429 				if (groupName == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
430 				{
431 					ResourceWithGroupMap::iterator iter = mResourcesWithGroup.begin();
432 					ResourceWithGroupMap::iterator iterE = mResourcesWithGroup.end();
433 					for ( ; iter != iterE ; ++iter )
434 					{
435 						ResourceMap::iterator resMapIt = iter->second.find(name);
436 
437 						if( resMapIt != iter->second.end())
438 						{
439 							res = resMapIt->second;
440 							break;
441 						}
442 					}
443 				}
444 			}
445 		}
446 
447 		return res;
448     }
449     //-----------------------------------------------------------------------
getByHandle(ResourceHandle handle)450     ResourcePtr ResourceManager::getByHandle(ResourceHandle handle)
451     {
452         OGRE_LOCK_AUTO_MUTEX;
453 
454         ResourceHandleMap::iterator it = mResourcesByHandle.find(handle);
455         if (it == mResourcesByHandle.end())
456         {
457             return ResourcePtr();
458         }
459         else
460         {
461             return it->second;
462         }
463     }
464     //-----------------------------------------------------------------------
getNextHandle(void)465     ResourceHandle ResourceManager::getNextHandle(void)
466     {
467         // This is an atomic operation and hence needs no locking
468         return mNextHandle++;
469     }
470     //-----------------------------------------------------------------------
checkUsage(void)471     void ResourceManager::checkUsage(void)
472     {
473 		if (getMemoryUsage() > mMemoryBudget)
474 		{
475                     OGRE_LOCK_AUTO_MUTEX;
476 			// unload unreferenced resources until we are within our budget again
477 			const bool reloadableOnly = true;
478 			ResourceMap::iterator i, iend;
479 			iend = mResources.end();
480 			for (i = mResources.begin(); i != iend && getMemoryUsage() > mMemoryBudget; ++i)
481 			{
482 				// A use count of 3 means that only RGM and RM have references
483 				// RGM has one (this one) and RM has 2 (by name and by handle)
484 				if (i->second.useCount() == ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS)
485 				{
486 					Resource* res = i->second.get();
487 					if (!reloadableOnly || res->isReloadable())
488 					{
489 						res->unload();
490 					}
491 				}
492 			}
493 		}
494 	}
495 	//-----------------------------------------------------------------------
_notifyResourceTouched(Resource * res)496 	void ResourceManager::_notifyResourceTouched(Resource* res)
497 	{
498 		// TODO
499 	}
500 	//-----------------------------------------------------------------------
_notifyResourceLoaded(Resource * res)501 	void ResourceManager::_notifyResourceLoaded(Resource* res)
502 	{
503 		mMemoryUsage += res->getSize();
504 		checkUsage();
505 	}
506 	//-----------------------------------------------------------------------
_notifyResourceUnloaded(Resource * res)507 	void ResourceManager::_notifyResourceUnloaded(Resource* res)
508 	{
509 		mMemoryUsage -= res->getSize();
510 	}
511 	//---------------------------------------------------------------------
getResourcePool(const String & name)512 	ResourceManager::ResourcePool* ResourceManager::getResourcePool(const String& name)
513 	{
514         OGRE_LOCK_AUTO_MUTEX;
515 
516 		ResourcePoolMap::iterator i = mResourcePoolMap.find(name);
517 		if (i == mResourcePoolMap.end())
518 		{
519 			i = mResourcePoolMap.insert(ResourcePoolMap::value_type(name,
520 				OGRE_NEW ResourcePool(name))).first;
521 		}
522 		return i->second;
523 
524 	}
525 	//---------------------------------------------------------------------
destroyResourcePool(ResourcePool * pool)526 	void ResourceManager::destroyResourcePool(ResourcePool* pool)
527 	{
528         if(!pool)
529             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Cannot destroy a null ResourcePool.", "ResourceManager::destroyResourcePool");
530 
531         OGRE_LOCK_AUTO_MUTEX;
532 
533 		ResourcePoolMap::iterator i = mResourcePoolMap.find(pool->getName());
534 		if (i != mResourcePoolMap.end())
535 			mResourcePoolMap.erase(i);
536 
537 		OGRE_DELETE pool;
538 
539 	}
540 	//---------------------------------------------------------------------
destroyResourcePool(const String & name)541 	void ResourceManager::destroyResourcePool(const String& name)
542 	{
543         OGRE_LOCK_AUTO_MUTEX;
544 
545 		ResourcePoolMap::iterator i = mResourcePoolMap.find(name);
546 		if (i != mResourcePoolMap.end())
547 		{
548 			OGRE_DELETE i->second;
549 			mResourcePoolMap.erase(i);
550 		}
551 
552 	}
553 	//---------------------------------------------------------------------
destroyAllResourcePools()554 	void ResourceManager::destroyAllResourcePools()
555 	{
556         OGRE_LOCK_AUTO_MUTEX;
557 
558 		for (ResourcePoolMap::iterator i = mResourcePoolMap.begin();
559 			i != mResourcePoolMap.end(); ++i)
560 			OGRE_DELETE i->second;
561 
562 		mResourcePoolMap.clear();
563 	}
564 	//-----------------------------------------------------------------------
565 	//---------------------------------------------------------------------
ResourcePool(const String & name)566 	ResourceManager::ResourcePool::ResourcePool(const String& name)
567 		: mName(name)
568 	{
569 
570 	}
571 	//---------------------------------------------------------------------
~ResourcePool()572 	ResourceManager::ResourcePool::~ResourcePool()
573 	{
574 		clear();
575 	}
576 	//---------------------------------------------------------------------
getName() const577 	const String& ResourceManager::ResourcePool::getName() const
578 	{
579 		return mName;
580 	}
581 	//---------------------------------------------------------------------
clear()582 	void ResourceManager::ResourcePool::clear()
583 	{
584             OGRE_LOCK_AUTO_MUTEX;
585 		for (ItemList::iterator i = mItems.begin(); i != mItems.end(); ++i)
586 		{
587 			(*i)->getCreator()->remove((*i)->getHandle());
588 		}
589 		mItems.clear();
590 	}
591 }
592 
593 
594 
595 
596