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 // Ogre includes
29 #include "OgreStableHeaders.h"
30 
31 #include "OgreResource.h"
32 #include "OgreResourceManager.h"
33 #include "OgreResourceBackgroundQueue.h"
34 #include "OgreLogManager.h"
35 #include "OgreException.h"
36 
37 namespace Ogre
38 {
39 	//-----------------------------------------------------------------------
Resource(ResourceManager * creator,const String & name,ResourceHandle handle,const String & group,bool isManual,ManualResourceLoader * loader)40 	Resource::Resource(ResourceManager* creator, const String& name, ResourceHandle handle,
41 		const String& group, bool isManual, ManualResourceLoader* loader)
42 		: mCreator(creator), mName(name), mGroup(group), mHandle(handle),
43 		mLoadingState(LOADSTATE_UNLOADED), mIsBackgroundLoaded(false),
44 		mSize(0), mIsManual(isManual), mLoader(loader), mStateCount(0)
45 	{
46 	}
47 	//-----------------------------------------------------------------------
~Resource()48 	Resource::~Resource()
49 	{
50 	}
51 	//-----------------------------------------------------------------------
escalateLoading()52 	void Resource::escalateLoading()
53 	{
54 		// Just call load as if this is the background thread, locking on
55 		// load status will prevent race conditions
56 		load(true);
57 		_fireLoadingComplete(true);
58 	}
59 	//-----------------------------------------------------------------------
prepare(bool background)60 	void Resource::prepare(bool background)
61 	{
62         // quick check that avoids any synchronisation
63         LoadingState old = mLoadingState.get();
64         if (old != LOADSTATE_UNLOADED && old != LOADSTATE_PREPARING) return;
65 
66         // atomically do slower check to make absolutely sure,
67         // and set the load state to PREPARING
68 		if (!mLoadingState.cas(LOADSTATE_UNLOADED,LOADSTATE_PREPARING))
69 		{
70 			while( mLoadingState.get() == LOADSTATE_PREPARING )
71 			{
72                             OGRE_LOCK_AUTO_MUTEX;
73 			}
74 
75 			LoadingState state = mLoadingState.get();
76 			if( state != LOADSTATE_PREPARED && state != LOADSTATE_LOADING && state != LOADSTATE_LOADED )
77 			{
78 				OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Another thread failed in resource operation",
79 					"Resource::prepare");
80 			}
81 			return;
82 		}
83 
84 		// Scope lock for actual loading
85         try
86 		{
87 
88                     OGRE_LOCK_AUTO_MUTEX;
89 
90 			if (mIsManual)
91 			{
92                 if (mLoader)
93 				{
94 					mLoader->prepareResource(this);
95 				}
96 				else
97 				{
98 					// Warn that this resource is not reloadable
99 					LogManager::getSingleton().stream(LML_TRIVIAL)
100 						<< "WARNING: " << mCreator->getResourceType()
101 						<< " instance '" << mName << "' was defined as manually "
102 						<< "loaded, but no manual loader was provided. This Resource "
103 						<< "will be lost if it has to be reloaded.";
104 				}
105 			}
106 			else
107 			{
108 				if (mGroup == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
109 				{
110 					// Derive resource group
111 					changeGroupOwnership(
112 						ResourceGroupManager::getSingleton()
113 						.findGroupContainingResource(mName));
114 				}
115 				prepareImpl();
116 			}
117 		}
118         catch (...)
119         {
120             mLoadingState.set(LOADSTATE_UNLOADED);
121             throw;
122         }
123 
124         mLoadingState.set(LOADSTATE_PREPARED);
125 
126 		// Since we don't distinguish between GPU and CPU RAM, this
127 		// seems pointless
128 		//if(mCreator)
129 		//	mCreator->_notifyResourcePrepared(this);
130 
131 		// Fire events (if not background)
132 		if (!background)
133 			_firePreparingComplete(false);
134 
135 
136 	}
137 	//---------------------------------------------------------------------
load(bool background)138     void Resource::load(bool background)
139 	{
140 		// Early-out without lock (mitigate perf cost of ensuring loaded)
141 		// Don't load if:
142 		// 1. We're already loaded
143 		// 2. Another thread is loading right now
144 		// 3. We're marked for background loading and this is not the background
145 		//    loading thread we're being called by
146 
147         if (mIsBackgroundLoaded && !background) return;
148 
149 		// This next section is to deal with cases where 2 threads are fighting over
150 		// who gets to prepare / load - this will only usually happen if loading is escalated
151 		bool keepChecking = true;
152 		LoadingState old = LOADSTATE_UNLOADED;
153 		while (keepChecking)
154 		{
155 			// quick check that avoids any synchronisation
156 			old = mLoadingState.get();
157 
158 			if ( old == LOADSTATE_PREPARING )
159 			{
160 				while( mLoadingState.get() == LOADSTATE_PREPARING )
161 				{
162                                     OGRE_LOCK_AUTO_MUTEX;
163 				}
164 				old = mLoadingState.get();
165 			}
166 
167 			if (old!=LOADSTATE_UNLOADED && old!=LOADSTATE_PREPARED && old!=LOADSTATE_LOADING) return;
168 
169 			// atomically do slower check to make absolutely sure,
170 			// and set the load state to LOADING
171 			if (old==LOADSTATE_LOADING || !mLoadingState.cas(old,LOADSTATE_LOADING))
172 			{
173 				while( mLoadingState.get() == LOADSTATE_LOADING )
174 				{
175                                     OGRE_LOCK_AUTO_MUTEX;
176 				}
177 
178 				LoadingState state = mLoadingState.get();
179 				if( state == LOADSTATE_PREPARED || state == LOADSTATE_PREPARING )
180 				{
181 					// another thread is preparing, loop around
182 					continue;
183 				}
184 				else if( state != LOADSTATE_LOADED )
185 				{
186 					OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Another thread failed in resource operation",
187 						"Resource::load");
188 				}
189 				return;
190 			}
191 			keepChecking = false;
192 		}
193 
194 		// Scope lock for actual loading
195         try
196 		{
197 
198                     OGRE_LOCK_AUTO_MUTEX;
199 
200 
201 
202 			if (mIsManual)
203 			{
204                 preLoadImpl();
205 				// Load from manual loader
206 				if (mLoader)
207 				{
208 					mLoader->loadResource(this);
209 				}
210 				else
211 				{
212 					// Warn that this resource is not reloadable
213 					LogManager::getSingleton().stream(LML_TRIVIAL)
214 						<< "WARNING: " << mCreator->getResourceType()
215 						<< " instance '" << mName << "' was defined as manually "
216 						<< "loaded, but no manual loader was provided. This Resource "
217 						<< "will be lost if it has to be reloaded.";
218 				}
219                 postLoadImpl();
220 			}
221 			else
222 			{
223 
224                 if (old==LOADSTATE_UNLOADED)
225                     prepareImpl();
226 
227                 preLoadImpl();
228 
229 				if (mGroup == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
230 				{
231 					// Derive resource group
232 					changeGroupOwnership(
233 						ResourceGroupManager::getSingleton()
234 						.findGroupContainingResource(mName));
235 				}
236 
237 				loadImpl();
238 
239                 postLoadImpl();
240 			}
241 
242 			// Calculate resource size
243 			mSize = calculateSize();
244 
245 		}
246         catch (...)
247         {
248             // Reset loading in-progress flag, in case failed for some reason.
249             // We reset it to UNLOADED because the only other case is when
250             // old == PREPARED in which case the loadImpl should wipe out
251             // any prepared data since it might be invalid.
252             mLoadingState.set(LOADSTATE_UNLOADED);
253             // Re-throw
254             throw;
255         }
256 
257         mLoadingState.set(LOADSTATE_LOADED);
258         _dirtyState();
259 
260 		// Notify manager
261 		if(mCreator)
262 			mCreator->_notifyResourceLoaded(this);
263 
264 		// Fire events, if not background
265 		if (!background)
266 			_fireLoadingComplete(false);
267 
268 
269 	}
270     //---------------------------------------------------------------------
calculateSize(void) const271     size_t Resource::calculateSize(void) const
272     {
273         size_t memSize = 0;
274         memSize += sizeof(ResourceManager);
275         memSize += sizeof(ManualResourceLoader);
276         memSize += sizeof(ResourceHandle);
277         memSize += mName.size() * sizeof(char);
278         memSize += mGroup.size() * sizeof(char);
279         memSize += mOrigin.size() * sizeof(char);
280         memSize += sizeof(size_t) * 2;
281         memSize += sizeof(bool) * 2;
282         memSize += sizeof(Listener) * mListenerList.size();
283         memSize += sizeof(AtomicScalar<LoadingState>);
284 
285         return memSize;
286     }
287 	//---------------------------------------------------------------------
_dirtyState()288 	void Resource::_dirtyState()
289 	{
290 		// don't worry about threading here, count only ever increases so
291 		// doesn't matter if we get a lost increment (one is enough)
292 		++mStateCount;
293 	}
294 	//-----------------------------------------------------------------------
changeGroupOwnership(const String & newGroup)295 	void Resource::changeGroupOwnership(const String& newGroup)
296 	{
297 		if (mGroup != newGroup)
298 		{
299 			String oldGroup = mGroup;
300 			mGroup = newGroup;
301 			ResourceGroupManager::getSingleton()
302 				._notifyResourceGroupChanged(oldGroup, this);
303 		}
304 	}
305 	//-----------------------------------------------------------------------
unload(void)306 	void Resource::unload(void)
307 	{
308 		// Early-out without lock (mitigate perf cost of ensuring unloaded)
309         LoadingState old = mLoadingState.get();
310         if (old!=LOADSTATE_LOADED && old!=LOADSTATE_PREPARED) return;
311 
312 
313         if (!mLoadingState.cas(old,LOADSTATE_UNLOADING)) return;
314 
315 		// Scope lock for actual unload
316 		{
317                     OGRE_LOCK_AUTO_MUTEX;
318             if (old==LOADSTATE_PREPARED) {
319                 unprepareImpl();
320             } else {
321                 preUnloadImpl();
322                 unloadImpl();
323                 postUnloadImpl();
324             }
325 		}
326 
327         mLoadingState.set(LOADSTATE_UNLOADED);
328 
329 		// Notify manager
330 		// Note if we have gone from PREPARED to UNLOADED, then we haven't actually
331 		// unloaded, i.e. there is no memory freed on the GPU.
332 		if(old==LOADSTATE_LOADED && mCreator)
333 			mCreator->_notifyResourceUnloaded(this);
334 
335 		_fireUnloadingComplete();
336 
337 
338 	}
339 	//-----------------------------------------------------------------------
reload(void)340 	void Resource::reload(void)
341 	{
342             OGRE_LOCK_AUTO_MUTEX;
343 		if (mLoadingState.get() == LOADSTATE_LOADED)
344 		{
345 			unload();
346 			load();
347 		}
348 	}
349 	//-----------------------------------------------------------------------
touch(void)350 	void Resource::touch(void)
351 	{
352         // make sure loaded
353         load();
354 
355 		if(mCreator)
356 			mCreator->_notifyResourceTouched(this);
357 	}
358 	//-----------------------------------------------------------------------
addListener(Resource::Listener * lis)359 	void Resource::addListener(Resource::Listener* lis)
360 	{
361             OGRE_LOCK_MUTEX(mListenerListMutex);
362 		mListenerList.insert(lis);
363 	}
364 	//-----------------------------------------------------------------------
removeListener(Resource::Listener * lis)365 	void Resource::removeListener(Resource::Listener* lis)
366 	{
367 		// O(n) but not called very often
368             OGRE_LOCK_MUTEX(mListenerListMutex);
369 		mListenerList.erase(lis);
370 	}
371 	//-----------------------------------------------------------------------
_fireLoadingComplete(bool wasBackgroundLoaded)372 	void Resource::_fireLoadingComplete(bool wasBackgroundLoaded)
373 	{
374 		// Lock the listener list
375             OGRE_LOCK_MUTEX(mListenerListMutex);
376 		for (ListenerList::iterator i = mListenerList.begin();
377 			i != mListenerList.end(); ++i)
378 		{
379 			// deprecated call
380 			if (wasBackgroundLoaded)
381 				(*i)->backgroundLoadingComplete(this);
382 
383 			(*i)->loadingComplete(this);
384 		}
385 	}
386 	//-----------------------------------------------------------------------
_firePreparingComplete(bool wasBackgroundLoaded)387 	void Resource::_firePreparingComplete(bool wasBackgroundLoaded)
388 	{
389 		// Lock the listener list
390             OGRE_LOCK_MUTEX(mListenerListMutex);
391 		for (ListenerList::iterator i = mListenerList.begin();
392 			i != mListenerList.end(); ++i)
393 		{
394 			// deprecated call
395 			if (wasBackgroundLoaded)
396 				(*i)->backgroundPreparingComplete(this);
397 
398 			(*i)->preparingComplete(this);
399 
400 		}
401 	}
402 	//-----------------------------------------------------------------------
_fireUnloadingComplete(void)403 	void Resource::_fireUnloadingComplete(void)
404 	{
405 		// Lock the listener list
406             OGRE_LOCK_MUTEX(mListenerListMutex);
407 			for (ListenerList::iterator i = mListenerList.begin();
408 				i != mListenerList.end(); ++i)
409 			{
410 
411 				(*i)->unloadingComplete(this);
412 
413 			}
414 	}
415 
416 }
417