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