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/
7 Copyright (c) 2000-2006 Torus Knot Software Ltd
8 Also see acknowledgements in Readme.html
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License along with
20 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22 http://www.gnu.org/copyleft/lesser.txt.
24 You may alternatively use this source under the terms of a specific version of
25 the OGRE Unrestricted License provided you have obtained such a license from
26 Torus Knot Software Ltd.
27 -----------------------------------------------------------------------------
29 #include "OgreStableHeaders.h"
30 #include "OgreResourceBackgroundQueue.h"
31 #include "OgreLogManager.h"
32 #include "OgreException.h"
33 #include "OgreResourceGroupManager.h"
34 #include "OgreResourceManager.h"
36 #include "OgreRenderSystem.h"
40 //------------------------------------------------------------------------
41 //-----------------------------------------------------------------------
42 template<> ResourceBackgroundQueue
* Singleton
<ResourceBackgroundQueue
>::ms_Singleton
= 0;
43 ResourceBackgroundQueue
* ResourceBackgroundQueue::getSingletonPtr(void)
47 ResourceBackgroundQueue
& ResourceBackgroundQueue::getSingleton(void)
49 assert( ms_Singleton
); return ( *ms_Singleton
);
51 //-----------------------------------------------------------------------
52 //------------------------------------------------------------------------
53 ResourceBackgroundQueue::ResourceBackgroundQueue()
54 :mNextTicketID(0), mStartThread(true), mThread(0)
55 #if OGRE_THREAD_SUPPORT
56 , mShuttingDown(false)
60 //------------------------------------------------------------------------
61 ResourceBackgroundQueue::~ResourceBackgroundQueue()
65 //------------------------------------------------------------------------
66 void ResourceBackgroundQueue::initialise(void)
68 #if OGRE_THREAD_SUPPORT
73 mShuttingDown
= false;
76 #if OGRE_THREAD_SUPPORT == 1
77 RenderSystem
* rs
= Root::getSingleton().getRenderSystem();
80 LogManager::getSingleton().logMessage(
81 "ResourceBackgroundQueue - threading enabled, starting own thread");
83 OGRE_LOCK_MUTEX_NAMED(initMutex
, initLock
)
85 #if OGRE_THREAD_SUPPORT == 1
86 // Call thread creation pre-hook
87 rs
->preExtraThreadsStarted();
90 mThread
= OGRE_NEW_T(boost::thread
, MEMCATEGORY_RESOURCE
)(
91 boost::function0
<void>(&ResourceBackgroundQueue::threadFunc
));
92 // Wait for init to finish before allowing main thread to continue
93 // this releases the initMutex until notified
94 OGRE_THREAD_WAIT(initSync
, initLock
)
96 #if OGRE_THREAD_SUPPORT == 1
97 // Call thread creation post-hook
98 rs
->postExtraThreadsStarted();
105 LogManager::getSingleton().logMessage(
106 "ResourceBackgroundQueue - threading enabled, user thread");
109 LogManager::getSingleton().logMessage(
110 "ResourceBackgroundQueue - threading disabled");
113 //------------------------------------------------------------------------
114 void ResourceBackgroundQueue::shutdown(void)
116 #if OGRE_THREAD_SUPPORT
119 // Put a shutdown request on the queue
121 req
.type
= RT_SHUTDOWN
;
123 // Wait for thread to finish
125 OGRE_DELETE_T(mThread
, thread
, MEMCATEGORY_RESOURCE
);
127 mRequestQueue
.clear();
128 mRequestTicketMap
.clear();
132 //------------------------------------------------------------------------
133 BackgroundProcessTicket
ResourceBackgroundQueue::initialiseResourceGroup(
134 const String
& name
, ResourceBackgroundQueue::Listener
* listener
)
136 #if OGRE_THREAD_SUPPORT
137 if (!mThread
&& mStartThread
)
139 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
140 "Thread not initialised",
141 "ResourceBackgroundQueue::initialiseResourceGroup");
145 req
.type
= RT_INITIALISE_GROUP
;
146 req
.groupName
= name
;
147 req
.listener
= listener
;
148 return addRequest(req
);
151 ResourceGroupManager::getSingleton().initialiseResourceGroup(name
);
155 //------------------------------------------------------------------------
156 BackgroundProcessTicket
157 ResourceBackgroundQueue::initialiseAllResourceGroups(
158 ResourceBackgroundQueue::Listener
* listener
)
160 #if OGRE_THREAD_SUPPORT
161 if (!mThread
&& mStartThread
)
163 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
164 "Thread not initialised",
165 "ResourceBackgroundQueue::initialiseAllResourceGroups");
169 req
.type
= RT_INITIALISE_ALL_GROUPS
;
170 req
.listener
= listener
;
171 return addRequest(req
);
174 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
178 //------------------------------------------------------------------------
179 BackgroundProcessTicket
ResourceBackgroundQueue::prepareResourceGroup(
180 const String
& name
, ResourceBackgroundQueue::Listener
* listener
)
182 #if OGRE_THREAD_SUPPORT
183 if (!mThread
&& mStartThread
)
185 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
186 "Thread not initialised",
187 "ResourceBackgroundQueue::prepareResourceGroup");
191 req
.type
= RT_PREPARE_GROUP
;
192 req
.groupName
= name
;
193 req
.listener
= listener
;
194 return addRequest(req
);
197 ResourceGroupManager::getSingleton().prepareResourceGroup(name
);
201 //------------------------------------------------------------------------
202 BackgroundProcessTicket
ResourceBackgroundQueue::loadResourceGroup(
203 const String
& name
, ResourceBackgroundQueue::Listener
* listener
)
205 #if OGRE_THREAD_SUPPORT
206 if (!mThread
&& mStartThread
)
208 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
209 "Thread not initialised",
210 "ResourceBackgroundQueue::loadResourceGroup");
214 req
.type
= RT_LOAD_GROUP
;
215 req
.groupName
= name
;
216 req
.listener
= listener
;
217 return addRequest(req
);
220 ResourceGroupManager::getSingleton().loadResourceGroup(name
);
224 //------------------------------------------------------------------------
225 BackgroundProcessTicket
ResourceBackgroundQueue::prepare(
226 const String
& resType
, const String
& name
,
227 const String
& group
, bool isManual
,
228 ManualResourceLoader
* loader
,
229 const NameValuePairList
* loadParams
,
230 ResourceBackgroundQueue::Listener
* listener
)
232 #if OGRE_THREAD_SUPPORT
233 if (!mThread
&& mStartThread
)
235 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
236 "Thread not initialised",
237 "ResourceBackgroundQueue::prepare");
241 req
.type
= RT_PREPARE_RESOURCE
;
242 req
.resourceType
= resType
;
243 req
.resourceName
= name
;
244 req
.groupName
= group
;
245 req
.isManual
= isManual
;
247 req
.loadParams
= loadParams
;
248 req
.listener
= listener
;
249 return addRequest(req
);
252 ResourceManager
* rm
=
253 ResourceGroupManager::getSingleton()._getResourceManager(resType
);
254 rm
->prepare(name
, group
, isManual
, loader
, loadParams
);
258 //------------------------------------------------------------------------
259 BackgroundProcessTicket
ResourceBackgroundQueue::load(
260 const String
& resType
, const String
& name
,
261 const String
& group
, bool isManual
,
262 ManualResourceLoader
* loader
,
263 const NameValuePairList
* loadParams
,
264 ResourceBackgroundQueue::Listener
* listener
)
266 #if OGRE_THREAD_SUPPORT
267 if (!mThread
&& mStartThread
)
269 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
270 "Thread not initialised",
271 "ResourceBackgroundQueue::load");
275 req
.type
= RT_LOAD_RESOURCE
;
276 req
.resourceType
= resType
;
277 req
.resourceName
= name
;
278 req
.groupName
= group
;
279 req
.isManual
= isManual
;
281 req
.loadParams
= loadParams
;
282 req
.listener
= listener
;
283 return addRequest(req
);
286 ResourceManager
* rm
=
287 ResourceGroupManager::getSingleton()._getResourceManager(resType
);
288 rm
->load(name
, group
, isManual
, loader
, loadParams
);
292 //---------------------------------------------------------------------
293 BackgroundProcessTicket
ResourceBackgroundQueue::unload(
294 const String
& resType
, const String
& name
, Listener
* listener
)
296 #if OGRE_THREAD_SUPPORT
297 if (!mThread
&& mStartThread
)
299 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
300 "Thread not initialised",
301 "ResourceBackgroundQueue::unload");
305 req
.type
= RT_UNLOAD_RESOURCE
;
306 req
.resourceType
= resType
;
307 req
.resourceName
= name
;
308 req
.listener
= listener
;
309 return addRequest(req
);
312 ResourceManager
* rm
=
313 ResourceGroupManager::getSingleton()._getResourceManager(resType
);
319 //---------------------------------------------------------------------
320 BackgroundProcessTicket
ResourceBackgroundQueue::unload(
321 const String
& resType
, ResourceHandle handle
, Listener
* listener
)
323 #if OGRE_THREAD_SUPPORT
324 if (!mThread
&& mStartThread
)
326 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
327 "Thread not initialised",
328 "ResourceBackgroundQueue::unload");
332 req
.type
= RT_UNLOAD_RESOURCE
;
333 req
.resourceType
= resType
;
334 req
.resourceHandle
= handle
;
335 req
.listener
= listener
;
336 return addRequest(req
);
339 ResourceManager
* rm
=
340 ResourceGroupManager::getSingleton()._getResourceManager(resType
);
346 //---------------------------------------------------------------------
347 BackgroundProcessTicket
ResourceBackgroundQueue::unloadResourceGroup(
348 const String
& name
, Listener
* listener
)
350 #if OGRE_THREAD_SUPPORT
351 if (!mThread
&& mStartThread
)
353 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR
,
354 "Thread not initialised",
355 "ResourceBackgroundQueue::unloadResourceGroup");
359 req
.type
= RT_UNLOAD_GROUP
;
360 req
.groupName
= name
;
361 req
.listener
= listener
;
362 return addRequest(req
);
365 ResourceGroupManager::getSingleton().unloadResourceGroup(name
);
370 //------------------------------------------------------------------------
371 bool ResourceBackgroundQueue::isProcessComplete(
372 BackgroundProcessTicket ticket
)
377 return mRequestTicketMap
.find(ticket
) == mRequestTicketMap
.end();
379 //------------------------------------------------------------------------
380 #if OGRE_THREAD_SUPPORT
381 BackgroundProcessTicket
ResourceBackgroundQueue::addRequest(Request
& req
)
386 req
.ticketID
= ++mNextTicketID
;
387 mRequestQueue
.push_back(req
);
388 Request
* requestInList
= &(mRequestQueue
.back());
389 mRequestTicketMap
[req
.ticketID
] = requestInList
;
391 // Notify to wake up loading thread
392 OGRE_THREAD_NOTIFY_ONE(mCondition
)
396 //------------------------------------------------------------------------
397 void ResourceBackgroundQueue::threadFunc(void)
399 // Background thread implementation
400 // Static (since no params allowed), so get instance
401 ResourceBackgroundQueue
& queueInstance
=
402 ResourceBackgroundQueue::getSingleton();
404 LogManager::getSingleton().logMessage("ResourceBackgroundQueue - thread starting.");
406 // Initialise the thread
407 queueInstance
._initThread();
409 // Spin forever until we're told to shut down
410 while (!queueInstance
.mShuttingDown
)
412 // Our thread will just wait when there is nothing on the queue
413 // _doNextQueuedBackgroundProcess won't do this since the thread
416 // Manual scope block just to define scope of lock
418 // Lock; note that 'mCondition.wait()' will free the lock
419 boost::recursive_mutex::scoped_lock
queueLock(
420 queueInstance
.OGRE_AUTO_MUTEX_NAME
);
421 if (queueInstance
.mRequestQueue
.empty())
423 // frees lock and suspends the thread
424 queueInstance
.mCondition
.wait(queueLock
);
426 // When we get back here, it's because we've been notified
427 // and thus the thread as been woken up. Lock has also been
429 } // release lock so queueing can be done while we process one request
431 queueInstance
._doNextQueuedBackgroundProcess();
436 LogManager::getSingleton().logMessage("ResourceBackgroundQueue - thread stopped.");
442 //-----------------------------------------------------------------------
443 void ResourceBackgroundQueue::_initThread()
445 // Register the calling thread with RenderSystem
446 // Note how we assume only one thread is processing the queue
447 #if OGRE_THREAD_SUPPORT == 1
448 Root::getSingleton().getRenderSystem()->registerThread();
451 // notify waiting thread(s)
452 OGRE_LOCK_MUTEX(initMutex
)
453 OGRE_THREAD_NOTIFY_ALL(initSync
)
457 //-----------------------------------------------------------------------
458 bool ResourceBackgroundQueue::_doNextQueuedBackgroundProcess()
463 // Manual scope block just to define scope of lock
466 // return false if nothing in the queue
467 if (mRequestQueue
.empty())
470 // Process one request
471 req
= &(mRequestQueue
.front());
472 } // release lock so queueing can be done while we process one request
473 // use of std::list means that references guarateed to remain valid
474 // we only allow one background thread
476 ResourceManager
* rm
= 0;
482 case RT_INITIALISE_GROUP
:
483 ResourceGroupManager::getSingleton().initialiseResourceGroup(
486 case RT_INITIALISE_ALL_GROUPS
:
487 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
489 case RT_PREPARE_GROUP
:
490 ResourceGroupManager::getSingleton().prepareResourceGroup(
494 #if OGRE_THREAD_SUPPORT == 2
495 ResourceGroupManager::getSingleton().prepareResourceGroup(
498 ResourceGroupManager::getSingleton().loadResourceGroup(
502 case RT_UNLOAD_GROUP
:
503 ResourceGroupManager::getSingleton().unloadResourceGroup(
506 case RT_PREPARE_RESOURCE
:
507 rm
= ResourceGroupManager::getSingleton()._getResourceManager(
509 rm
->prepare(req
->resourceName
, req
->groupName
, req
->isManual
,
510 req
->loader
, req
->loadParams
);
512 case RT_LOAD_RESOURCE
:
513 rm
= ResourceGroupManager::getSingleton()._getResourceManager(
515 #if OGRE_THREAD_SUPPORT == 2
516 rm
->prepare(req
->resourceName
, req
->groupName
, req
->isManual
,
517 req
->loader
, req
->loadParams
);
519 rm
->load(req
->resourceName
, req
->groupName
, req
->isManual
,
520 req
->loader
, req
->loadParams
);
523 case RT_UNLOAD_RESOURCE
:
524 rm
= ResourceGroupManager::getSingleton()._getResourceManager(
526 if (req
->resourceName
.empty())
527 rm
->unload(req
->resourceHandle
);
529 rm
->unload(req
->resourceName
);
533 #if OGRE_THREAD_SUPPORT
534 mShuttingDown
= true;
535 #if OGRE_THREAD_SUPPORT == 1
536 Root::getSingleton().getRenderSystem()->unregisterThread();
544 req
->result
.error
= true;
545 req
->result
.message
= e
.getFullDescription();
548 // Queue notification (don't do shutdown since not needed & listeners
549 // might be being destroyed too
550 if (req
->listener
&& req
->type
!= RT_SHUTDOWN
)
552 // Fire in-thread notification first
553 req
->listener
->operationCompletedInThread(req
->ticketID
, req
->result
);
554 // Then queue main thread notification
555 queueFireBackgroundOperationComplete(req
);
560 // re-lock to consume completed request
563 // Consume the ticket
564 mRequestTicketMap
.erase(req
->ticketID
);
565 mRequestQueue
.pop_front();
571 //-----------------------------------------------------------------------
572 void ResourceBackgroundQueue::_queueFireBackgroundLoadingComplete(Resource
* res
)
574 OGRE_LOCK_MUTEX(mNotificationQueueMutex
);
575 mNotificationQueue
.push_back(QueuedNotification(res
,true));
578 //-----------------------------------------------------------------------
579 void ResourceBackgroundQueue::_queueFireBackgroundPreparingComplete(Resource
* res
)
581 OGRE_LOCK_MUTEX(mNotificationQueueMutex
);
582 mNotificationQueue
.push_back(QueuedNotification(res
,false));
585 //-----------------------------------------------------------------------
586 void ResourceBackgroundQueue::queueFireBackgroundOperationComplete(
587 ResourceBackgroundQueue::Request
* req
)
589 OGRE_LOCK_MUTEX(mNotificationQueueMutex
);
590 mNotificationQueue
.push_back(QueuedNotification(*req
));
592 //------------------------------------------------------------------------
593 void ResourceBackgroundQueue::_fireOnFrameCallbacks()
595 OGRE_LOCK_MUTEX(mNotificationQueueMutex
);
596 for (NotificationQueue::iterator i
= mNotificationQueue
.begin();
597 i
!= mNotificationQueue
.end(); ++i
)
601 i
->resource
->_fireLoadingComplete();
603 i
->resource
->_firePreparingComplete();
606 const ResourceBackgroundQueue::Request
&r
= i
->req
;
607 #if OGRE_THREAD_SUPPORT == 2
608 if (r
.type
==RT_LOAD_RESOURCE
) {
609 ResourceManager
*rm
= ResourceGroupManager::getSingleton()
610 ._getResourceManager(r
.resourceType
);
611 rm
->load(r
.resourceName
, r
.groupName
, r
.isManual
, r
.loader
, r
.loadParams
);
612 } else if (r
.type
==RT_LOAD_GROUP
) {
613 ResourceGroupManager::getSingleton().loadResourceGroup(r
.groupName
);
616 r
.listener
->operationCompleted(r
.ticketID
, r
.result
);
619 mNotificationQueue
.clear();
622 //------------------------------------------------------------------------