History: Threading
Preview of version: 7
User comments
This is mainly just a rewording of this forum thread (year 2005) so go there for the full answer.
You should thread things that naturally fall into the background, ie to be spread over multiple frames. That's AI, buffering of streaming data, but probably not physics (because you need to keep it in sync with the frame rate if it's not going to look weird). None of these things are Ogre itself. Due to the fact that the graphics are the most noticeable thing, and that the GPU is asynchronous anyway, rendering processes are rarely farmed out to the background, so there's generally no need for thread safety. Projects which use threading thread non-graphical things.
The one case where it might be useful is background loading using the resource system. In Azathoth the resource system is (experimentally) thread safe if you set OGRE_THREAD_SUPPORT to 1, allowing you to load things in a background thread if you want. But that's only the resource system, nothing else is thread safe, and in my opinion has no need to be. We use boost::threads for this, personally it seemed a much better solution than anything else I've seen (it reminds me of Java threads).
Keep in mind you don't need threads to run systems at varying rates (keep countdowns, if time is up step the system, if not move on).
If you're using ogre for a game threading isn't really necessary (like sinbad said, graphics are already asynchronous).
For things like physics, multi-steps are often done (instead of updating the physics simulation 30 milliseconds, you might have it do 15 milliseconds 2 times. Physics simulations get less accurate with bigger updates, which doesn't apply to most other systems).
You wouldn't want to simply run a physics update multiple times for each graphics one, you should work it out with timers and countdowns.
I should point out that even though Ogre is not thread-safe, there is no reason why your application cannot use threads. It just means that you cannot have multiple threads which deal with Ogre.
As long as you make sure that only a single thread handles all your Ogre-related tasks, you are free to run your sound engine or AI scripts in other threads.
Example
This example is based on the Tutorial Framework and displays the ogre head. The position of the head is calculated in a new thread using boost. The result is used in main thread to move the head.
TutorialApplication.h
#ifndef __TutorialApplication_h_ #define __TutorialApplication_h_ #include "BaseApplication.h" #include <boost/thread/thread.hpp> #include <boost/thread/mutex.hpp> #include <boost/shared_ptr.hpp> #include <boost/random/linear_congruential.hpp> #include <boost/random/uniform_real.hpp> #include <boost/random/variate_generator.hpp> #include <boost/generator_iterator.hpp> class TutorialApplication : public BaseApplication { public: TutorialApplication(void); virtual ~TutorialApplication(void); // true if a stop was requested, false otherwise volatile bool m_bStopRequested; // thread pointer boost::shared_ptr<boost::thread> m_Thread; // Mutext for data shareing boost::mutex m_Mutex; // share data Ogre::Vector3 m_headPosition; protected: virtual void createScene(void); virtual void destroyScene(void); virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt); Ogre::SceneNode* m_pHeadNode; private: void run(); void createPosition(); }; #endif // #ifndef __TutorialApplication_h_
TutorialApplication.cpp
#include "TutorialApplication.h" //------------------------------------------------------------------------------------- TutorialApplication::TutorialApplication(void) { m_pHeadNode = NULL; m_bStopRequested = false; } //------------------------------------------------------------------------------------- TutorialApplication::~TutorialApplication(void) { } //------------------------------------------------------------------------------------- void TutorialApplication::createScene(void) { // Set the scene's ambient light mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f)); // Create an Entity Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); // Create a SceneNode and attach the Entity to it m_pHeadNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode"); m_pHeadNode->attachObject(ogreHead); // Create a Light and set its position Ogre::Light* light = mSceneMgr->createLight("MainLight"); light->setPosition(20.0f, 80.0f, 50.0f); // Create the thread and start work assert(!m_Thread); m_Thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&TutorialApplication::run, this))); } //------------------------------------------------------------------------------------- void TutorialApplication::destroyScene(void) { // Stops the thread assert(m_Thread); m_bStopRequested = true; m_Thread->join(); // Destroy scene BaseApplication::destroyScene(); } //------------------------------------------------------------------------------------- // See Basic Tutorial 4 bool TutorialApplication::frameRenderingQueued(const Ogre::FrameEvent& evt) { bool ret = BaseApplication::frameRenderingQueued(evt); // Update position of ogre head if(m_pHeadNode != NULL) { // Lock variable boost::unique_lock<boost::mutex> lock(m_Mutex); // Process data in main thread m_pHeadNode->setPosition(m_headPosition); } return ret; } // Unlock //------------------------------------------------------------------------------------- void TutorialApplication::run() { Ogre::LogManager::getSingletonPtr()->logMessage("*** Entering new thread ***"); while(!m_bStopRequested) { boost::this_thread::sleep(boost::posix_time::seconds(1)); // use a seperate function to lock variable and unlock it with returning createPosition(); } Ogre::LogManager::getSingletonPtr()->logMessage("*** Leaving new thread ***"); } //------------------------------------------------------------------------------------- void TutorialApplication::createPosition() { // Init random generator boost::minstd_rand generator(static_cast<unsigned int>(std::time(0))); boost::uniform_real<> uni_dist(0, 100); boost::variate_generator<boost::minstd_rand&, boost::uniform_real<>> uni(generator, uni_dist); // Lock variable boost::unique_lock<boost::mutex> lock(m_Mutex); // Set new position m_headPosition = Ogre::Vector3(uni(), uni(), uni()); } // Unlock