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-2006 Torus Knot Software Ltd |
---|
8 | Also see acknowledgements in Readme.html |
---|
9 | |
---|
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 |
---|
13 | version. |
---|
14 | |
---|
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. |
---|
18 | |
---|
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. |
---|
23 | |
---|
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 | ----------------------------------------------------------------------------- |
---|
28 | */ |
---|
29 | #ifndef __ResourceBackgroundQueue_H__ |
---|
30 | #define __ResourceBackgroundQueue_H__ |
---|
31 | |
---|
32 | |
---|
33 | #include "OgrePrerequisites.h" |
---|
34 | #include "OgreCommon.h" |
---|
35 | #include "OgreSingleton.h" |
---|
36 | #include "OgreResource.h" |
---|
37 | |
---|
38 | #if OGRE_THREAD_SUPPORT |
---|
39 | # include <boost/thread/thread.hpp> |
---|
40 | # include <boost/thread/condition.hpp> |
---|
41 | #endif |
---|
42 | |
---|
43 | namespace Ogre { |
---|
44 | |
---|
45 | /// Identifier of a background process |
---|
46 | typedef unsigned long BackgroundProcessTicket; |
---|
47 | |
---|
48 | /** This class is used to perform Resource operations in a |
---|
49 | background thread. |
---|
50 | @remarks |
---|
51 | If threading is enabled, Ogre will create a single background thread |
---|
52 | which can be used to load / unload resources in parallel. Only one |
---|
53 | resource will be processed at once in this background thread, but it |
---|
54 | will be in parallel with the main thread. |
---|
55 | @par |
---|
56 | The general approach here is that on requesting a background resource |
---|
57 | process, your request is placed on a queue ready for the background |
---|
58 | thread to be picked up, and you will get a 'ticket' back, identifying |
---|
59 | the request. Your call will then return and your thread can |
---|
60 | proceed, knowing that at some point in the background the operation wil |
---|
61 | be performed. In it's own thread, the resource operation will be |
---|
62 | performed, and once finished the ticket will be marked as complete. |
---|
63 | You can check the status of tickets by calling isProcessComplete() |
---|
64 | from your queueing thread. It is also possible to get immediate |
---|
65 | callbacks on completion, but these callbacks happen in the background |
---|
66 | loading thread (not your calling thread), so should only be used if you |
---|
67 | really understand multithreading. |
---|
68 | @par |
---|
69 | By default, when threading is enabled this class will start its own |
---|
70 | separate thread to perform the actual loading. However, if you would |
---|
71 | prefer to use your own existing thread to perform the background load, |
---|
72 | then be sure to call setStartBackgroundThread(false) before initialise() is |
---|
73 | called by Root::initialise. Your own thread should call _initThread |
---|
74 | immediately on startup, before any resources are loaded at all, and |
---|
75 | _doNextQueuedBackgroundProcess to process background requests. |
---|
76 | @note |
---|
77 | This class will only perform tasks in a background thread if |
---|
78 | OGRE_THREAD_SUPPORT is defined to be 1. Otherwise, all methods will |
---|
79 | call their exact equivalents in ResourceGroupManager synchronously. |
---|
80 | */ |
---|
81 | class _OgreExport ResourceBackgroundQueue : public Singleton<ResourceBackgroundQueue> |
---|
82 | { |
---|
83 | public: |
---|
84 | /** This abstract listener interface lets you get notifications of |
---|
85 | completed background processes instead of having to poll ticket |
---|
86 | statuses. |
---|
87 | @note |
---|
88 | For simplicity, these callbacks are not issued direct from the background |
---|
89 | loading thread, they are queued themselves to be sent from the main thread |
---|
90 | so that you don't have to be concerned about thread safety. |
---|
91 | */ |
---|
92 | class _OgreExport Listener |
---|
93 | { |
---|
94 | public: |
---|
95 | /** Called when a requested operation completes, queued into main thread. |
---|
96 | @note |
---|
97 | For simplicity, this callback is not issued direct from the background |
---|
98 | loading thread, it is queued to be sent from the main thread |
---|
99 | so that you don't have to be concerned about thread safety. |
---|
100 | */ |
---|
101 | virtual void operationCompleted(BackgroundProcessTicket ticket) = 0; |
---|
102 | /** Called when a requested operation completes, immediate in background thread. |
---|
103 | @note |
---|
104 | This is the advanced version of the background operation notification, |
---|
105 | it happens immediately when the background operation is completed, and |
---|
106 | your callback is executed in the <b>background thread</b>. Therefore if |
---|
107 | you use this version, you have to be aware of thread safety issues |
---|
108 | and what you can and cannot do in your callback implementation. |
---|
109 | */ |
---|
110 | virtual void operationCompletedInThread(BackgroundProcessTicket ticket) {} |
---|
111 | /// Need virtual destructor in case subclasses use it |
---|
112 | virtual ~Listener() {} |
---|
113 | |
---|
114 | }; |
---|
115 | /// Init notification mutex (must lock before waiting on initCondition) |
---|
116 | OGRE_MUTEX(initMutex) |
---|
117 | /// Synchroniser token to wait / notify on thread init (public incase external thread) |
---|
118 | OGRE_THREAD_SYNCHRONISER(initSync); |
---|
119 | |
---|
120 | protected: |
---|
121 | /** Enumerates the type of requests */ |
---|
122 | enum RequestType |
---|
123 | { |
---|
124 | RT_INITIALISE_GROUP, |
---|
125 | RT_INITIALISE_ALL_GROUPS, |
---|
126 | RT_LOAD_GROUP, |
---|
127 | RT_LOAD_RESOURCE, |
---|
128 | RT_UNLOAD_GROUP, |
---|
129 | RT_UNLOAD_RESOURCE, |
---|
130 | RT_SHUTDOWN |
---|
131 | }; |
---|
132 | /** Encapsulates a queued request for the background queue */ |
---|
133 | struct Request |
---|
134 | { |
---|
135 | BackgroundProcessTicket ticketID; |
---|
136 | RequestType type; |
---|
137 | String resourceName; |
---|
138 | ResourceHandle resourceHandle; |
---|
139 | String resourceType; |
---|
140 | String groupName; |
---|
141 | bool isManual; |
---|
142 | ManualResourceLoader* loader; |
---|
143 | const NameValuePairList* loadParams; |
---|
144 | Listener* listener; |
---|
145 | }; |
---|
146 | typedef std::list<Request> RequestQueue; |
---|
147 | typedef std::map<BackgroundProcessTicket, Request*> RequestTicketMap; |
---|
148 | |
---|
149 | /// Queue of requests, used to store and order requests |
---|
150 | RequestQueue mRequestQueue; |
---|
151 | |
---|
152 | /// Request lookup by ticket |
---|
153 | RequestTicketMap mRequestTicketMap; |
---|
154 | |
---|
155 | /// Next ticket ID |
---|
156 | unsigned long mNextTicketID; |
---|
157 | |
---|
158 | /// Struct that holds details of queued notifications |
---|
159 | struct QueuedNotification |
---|
160 | { |
---|
161 | QueuedNotification(Resource::Listener* l, Resource* r) |
---|
162 | : resourceListener(l), resource(r), opListener(0), ticket(0) |
---|
163 | {} |
---|
164 | |
---|
165 | QueuedNotification(Listener* l, BackgroundProcessTicket t) |
---|
166 | : resourceListener(0), resource(0), opListener(l), ticket(t) |
---|
167 | {} |
---|
168 | |
---|
169 | // Type 1 - Resource::Listener kind |
---|
170 | Resource::Listener* resourceListener; |
---|
171 | Resource* resource; |
---|
172 | // Type 2 - ResourceBackgroundQueue::Listener kind |
---|
173 | Listener* opListener; |
---|
174 | BackgroundProcessTicket ticket; |
---|
175 | }; |
---|
176 | typedef std::list<QueuedNotification> NotificationQueue; |
---|
177 | /// Queued notifications of background loading being finished |
---|
178 | NotificationQueue mNotificationQueue; |
---|
179 | /// Mutex to protect the background event queue] |
---|
180 | OGRE_MUTEX(mNotificationQueueMutex) |
---|
181 | |
---|
182 | /// Whether this class should start it's own thread or not |
---|
183 | bool mStartThread; |
---|
184 | |
---|
185 | #if OGRE_THREAD_SUPPORT |
---|
186 | /// The single background thread which will process loading requests |
---|
187 | boost::thread* mThread; |
---|
188 | /// Synchroniser token to wait / notify on queue |
---|
189 | boost::condition mCondition; |
---|
190 | /// Thread function |
---|
191 | static void threadFunc(void); |
---|
192 | /// Internal method for adding a request; also assigns a ticketID |
---|
193 | BackgroundProcessTicket addRequest(Request& req); |
---|
194 | /// Thread shutdown? |
---|
195 | bool mShuttingDown; |
---|
196 | #else |
---|
197 | /// Dummy |
---|
198 | void* mThread; |
---|
199 | #endif |
---|
200 | |
---|
201 | /// Private mutex, not allowed to lock from outside |
---|
202 | OGRE_AUTO_MUTEX |
---|
203 | |
---|
204 | /** Queue the firing of the 'background loading complete' event to |
---|
205 | a Resource::Listener event. |
---|
206 | @remarks |
---|
207 | The purpose of this is to allow the background loading thread to |
---|
208 | call this method to queue the notification to listeners waiting on |
---|
209 | the background loading of a resource. Rather than allow the resource |
---|
210 | background loading thread to directly call these listeners, which |
---|
211 | would require all the listeners to be thread-safe, this method |
---|
212 | implements a thread-safe queue which can be processed in the main |
---|
213 | frame loop thread each frame to clear the events in a simpler |
---|
214 | manner. |
---|
215 | @param listener The listener to be notified |
---|
216 | @param ticket The ticket for the operation that has completed |
---|
217 | */ |
---|
218 | virtual void queueFireBackgroundOperationComplete(Listener* listener, |
---|
219 | BackgroundProcessTicket ticket); |
---|
220 | |
---|
221 | public: |
---|
222 | ResourceBackgroundQueue(); |
---|
223 | virtual ~ResourceBackgroundQueue(); |
---|
224 | |
---|
225 | /** Sets whether or not a thread should be created and started to handle |
---|
226 | the background loading, or whether a user thread will call the |
---|
227 | appropriate hooks. |
---|
228 | @remarks |
---|
229 | By default, a new thread will be started to handle the background |
---|
230 | load requests. However, the application may well have some threads |
---|
231 | of its own which is wishes to use to perform the background loading |
---|
232 | as well as other tasks (for example on most platforms there will be |
---|
233 | a fixed number of hardware threads which the application will wish |
---|
234 | to work within). Use this method to turn off the creation of a separate |
---|
235 | thread if you wish, and call the _doNextQueuedBackgroundProcess |
---|
236 | method from your own thread to process background requests. |
---|
237 | @note |
---|
238 | You <b>must</b> call this method prior to initialisation. Initialisation |
---|
239 | of this class is automatically done when Root::initialise is called. |
---|
240 | */ |
---|
241 | void setStartBackgroundThread(bool startThread) { mStartThread = startThread; } |
---|
242 | |
---|
243 | /** Gets whether or not a thread should be created and started to handle |
---|
244 | the background loading, or whether a user thread will call the |
---|
245 | appropriate hooks. |
---|
246 | */ |
---|
247 | bool getStartBackgroundThread(void) { return mStartThread; } |
---|
248 | /** Initialise the background queue system. |
---|
249 | @note Called automatically by Root::initialise. |
---|
250 | */ |
---|
251 | virtual void initialise(void); |
---|
252 | |
---|
253 | /** Shut down the background queue system. |
---|
254 | @note Called automatically by Root::shutdown. |
---|
255 | */ |
---|
256 | virtual void shutdown(void); |
---|
257 | |
---|
258 | /** Initialise a resource group in the background. |
---|
259 | @see ResourceGroupManager::initialiseResourceGroup |
---|
260 | @param name The name of the resource group to initialise |
---|
261 | @param listener Optional callback interface, take note of warnings in |
---|
262 | the header and only use if you understand them. |
---|
263 | @returns Ticket identifying the request, use isProcessComplete() to |
---|
264 | determine if completed if not using listener |
---|
265 | */ |
---|
266 | virtual BackgroundProcessTicket initialiseResourceGroup( |
---|
267 | const String& name, Listener* listener = 0); |
---|
268 | |
---|
269 | /** Initialise all resource groups which are yet to be initialised in |
---|
270 | the background. |
---|
271 | @see ResourceGroupManager::intialiseResourceGroup |
---|
272 | @param listener Optional callback interface, take note of warnings in |
---|
273 | the header and only use if you understand them. |
---|
274 | @returns Ticket identifying the request, use isProcessComplete() to |
---|
275 | determine if completed if not using listener |
---|
276 | */ |
---|
277 | virtual BackgroundProcessTicket initialiseAllResourceGroups( |
---|
278 | Listener* listener = 0); |
---|
279 | /** Loads a resource group in the background. |
---|
280 | @see ResourceGroupManager::loadResourceGroup |
---|
281 | @param name The name of the resource group to load |
---|
282 | @param listener Optional callback interface, take note of warnings in |
---|
283 | the header and only use if you understand them. |
---|
284 | @returns Ticket identifying the request, use isProcessComplete() to |
---|
285 | determine if completed if not using listener |
---|
286 | */ |
---|
287 | virtual BackgroundProcessTicket loadResourceGroup(const String& name, |
---|
288 | Listener* listener = 0); |
---|
289 | |
---|
290 | |
---|
291 | /** Unload a single resource in the background. |
---|
292 | @see ResourceManager::unload |
---|
293 | @param resType The type of the resource |
---|
294 | (from ResourceManager::getResourceType()) |
---|
295 | @param name The name of the Resource |
---|
296 | */ |
---|
297 | virtual BackgroundProcessTicket unload( |
---|
298 | const String& resType, const String& name, |
---|
299 | Listener* listener = 0); |
---|
300 | |
---|
301 | /** Unload a single resource in the background. |
---|
302 | @see ResourceManager::unload |
---|
303 | @param resType The type of the resource |
---|
304 | (from ResourceManager::getResourceType()) |
---|
305 | @param handle Handle to the resource |
---|
306 | */ |
---|
307 | virtual BackgroundProcessTicket unload( |
---|
308 | const String& resType, ResourceHandle handle, |
---|
309 | Listener* listener = 0); |
---|
310 | |
---|
311 | /** Unloads a resource group in the background. |
---|
312 | @see ResourceGroupManager::unloadResourceGroup |
---|
313 | @param name The name of the resource group to load |
---|
314 | @returns Ticket identifying the request, use isProcessComplete() to |
---|
315 | determine if completed if not using listener |
---|
316 | */ |
---|
317 | virtual BackgroundProcessTicket unloadResourceGroup(const String& name, |
---|
318 | Listener* listener = 0); |
---|
319 | |
---|
320 | |
---|
321 | /** Load a single resource in the background. |
---|
322 | @see ResourceManager::load |
---|
323 | @param resType The type of the resource |
---|
324 | (from ResourceManager::getResourceType()) |
---|
325 | @param name The name of the Resource |
---|
326 | @param group The resource group to which this resource will belong |
---|
327 | @param isManual Is the resource to be manually loaded? If so, you should |
---|
328 | provide a value for the loader parameter |
---|
329 | @param loader The manual loader which is to perform the required actions |
---|
330 | when this resource is loaded; only applicable when you specify true |
---|
331 | for the previous parameter. NOTE: must be thread safe!! |
---|
332 | @param loadParams Optional pointer to a list of name/value pairs |
---|
333 | containing loading parameters for this type of resource. Remember |
---|
334 | that this must have a lifespan longer than the return of this call! |
---|
335 | */ |
---|
336 | virtual BackgroundProcessTicket load( |
---|
337 | const String& resType, const String& name, |
---|
338 | const String& group, bool isManual = false, |
---|
339 | ManualResourceLoader* loader = 0, |
---|
340 | const NameValuePairList* loadParams = 0, |
---|
341 | Listener* listener = 0); |
---|
342 | /** Returns whether a previously queued process has completed or not. |
---|
343 | @remarks |
---|
344 | This method of checking that a background process has completed is |
---|
345 | the 'polling' approach. Each queued method takes an optional listener |
---|
346 | parameter to allow you to register a callback instead, which is |
---|
347 | arguably more efficient. |
---|
348 | @param ticket The ticket which was returned when the process was queued |
---|
349 | @returns true if process has completed (or if the ticket is |
---|
350 | unrecognised), false otherwise |
---|
351 | @note Tickets are not stored onced complete so do not accumulate over |
---|
352 | time. |
---|
353 | This is why a non-existent ticket will return 'true'. |
---|
354 | */ |
---|
355 | virtual bool isProcessComplete(BackgroundProcessTicket ticket); |
---|
356 | |
---|
357 | /** Process a single queued background operation. |
---|
358 | @remarks |
---|
359 | If you are using your own thread to perform background loading, calling |
---|
360 | this method from that thread triggers the processing of a single |
---|
361 | background loading request from the queue. This method will not |
---|
362 | return until the request has been fully processed. It also returns |
---|
363 | whether it did in fact process anything - if it returned false, there |
---|
364 | was nothing more in the queue. |
---|
365 | @note |
---|
366 | <b>Do not</b> call this method unless you are using your own thread |
---|
367 | to perform the background loading and called setStartBackgroundThread(false). |
---|
368 | You must only have one background loading thread. |
---|
369 | @returns true if a request was processed, false if the queue was empty. |
---|
370 | */ |
---|
371 | bool _doNextQueuedBackgroundProcess(); |
---|
372 | |
---|
373 | /** Initialise processing for a background thread. |
---|
374 | @remarks |
---|
375 | You must call this method if you use your own thread rather than |
---|
376 | letting this class create its own. Moreover, you must call it after |
---|
377 | initialise() and after you've started your own thread, but before |
---|
378 | any resources have been loaded. There are some |
---|
379 | per-thread tasks which have to be performed on some rendering APIs |
---|
380 | and it's important that they are done before rendering resources are |
---|
381 | created. |
---|
382 | @par |
---|
383 | You must call this method in your own background thread, not the main |
---|
384 | thread. It's important to block the main thread whilst this initialisation |
---|
385 | is happening, use an OGRE_THREAD_WAIT on the public initSync token |
---|
386 | after locking the initMutex. |
---|
387 | */ |
---|
388 | void _initThread(); |
---|
389 | |
---|
390 | /** Queue the firing of the 'background loading complete' event to |
---|
391 | a Resource::Listener event. |
---|
392 | @remarks |
---|
393 | The purpose of this is to allow the background loading thread to |
---|
394 | call this method to queue the notification to listeners waiting on |
---|
395 | the background loading of a resource. Rather than allow the resource |
---|
396 | background loading thread to directly call these listeners, which |
---|
397 | would require all the listeners to be thread-safe, this method |
---|
398 | implements a thread-safe queue which can be processed in the main |
---|
399 | frame loop thread each frame to clear the events in a simpler |
---|
400 | manner. |
---|
401 | @param listener The listener to be notified |
---|
402 | @param res The resource listened on |
---|
403 | */ |
---|
404 | virtual void _queueFireBackgroundLoadingComplete(Resource::Listener* listener, |
---|
405 | Resource* res); |
---|
406 | |
---|
407 | /** Fires all the queued events for background loaded resources. |
---|
408 | @remarks |
---|
409 | You should call this from the thread that runs the main frame loop |
---|
410 | to avoid having to make the receivers of this event thread-safe. |
---|
411 | If you use Ogre's built in frame loop you don't need to call this |
---|
412 | yourself. |
---|
413 | */ |
---|
414 | virtual void _fireBackgroundLoadingComplete(void); |
---|
415 | |
---|
416 | /** Override standard Singleton retrieval. |
---|
417 | @remarks |
---|
418 | Why do we do this? Well, it's because the Singleton |
---|
419 | implementation is in a .h file, which means it gets compiled |
---|
420 | into anybody who includes it. This is needed for the |
---|
421 | Singleton template to work, but we actually only want it |
---|
422 | compiled into the implementation of the class based on the |
---|
423 | Singleton, not all of them. If we don't change this, we get |
---|
424 | link errors when trying to use the Singleton-based class from |
---|
425 | an outside dll. |
---|
426 | @par |
---|
427 | This method just delegates to the template version anyway, |
---|
428 | but the implementation stays in this single compilation unit, |
---|
429 | preventing link errors. |
---|
430 | */ |
---|
431 | static ResourceBackgroundQueue& getSingleton(void); |
---|
432 | /** Override standard Singleton retrieval. |
---|
433 | @remarks |
---|
434 | Why do we do this? Well, it's because the Singleton |
---|
435 | implementation is in a .h file, which means it gets compiled |
---|
436 | into anybody who includes it. This is needed for the |
---|
437 | Singleton template to work, but we actually only want it |
---|
438 | compiled into the implementation of the class based on the |
---|
439 | Singleton, not all of them. If we don't change this, we get |
---|
440 | link errors when trying to use the Singleton-based class from |
---|
441 | an outside dll. |
---|
442 | @par |
---|
443 | This method just delegates to the template version anyway, |
---|
444 | but the implementation stays in this single compilation unit, |
---|
445 | preventing link errors. |
---|
446 | */ |
---|
447 | static ResourceBackgroundQueue* getSingletonPtr(void); |
---|
448 | |
---|
449 | |
---|
450 | }; |
---|
451 | |
---|
452 | |
---|
453 | } |
---|
454 | |
---|
455 | #endif |
---|
456 | |
---|